clicktype_query/
subquery.rs1use std::marker::PhantomData;
4use clicktype_core::traits::ClickTable;
5use crate::builder::QueryBuilder;
6
7pub struct Subquery<T: ClickTable> {
9 builder: QueryBuilder<T>,
10 alias: Option<String>,
11}
12
13impl<T: ClickTable> Subquery<T> {
14 pub fn from_builder(builder: QueryBuilder<T>) -> Self {
16 Self {
17 builder,
18 alias: None,
19 }
20 }
21
22 pub fn alias(mut self, alias: impl Into<String>) -> Self {
24 self.alias = Some(alias.into());
25 self
26 }
27
28 pub fn to_sql(&self) -> String {
30 let mut sql = format!("({})", self.builder.to_sql());
31 if let Some(alias) = &self.alias {
32 sql.push_str(" AS ");
33 sql.push_str(alias);
34 }
35 sql
36 }
37}
38
39pub struct CTE<T: ClickTable> {
41 name: String,
42 builder: QueryBuilder<T>,
43}
44
45impl<T: ClickTable> CTE<T> {
46 pub fn new(name: impl Into<String>, builder: QueryBuilder<T>) -> Self {
48 Self {
49 name: name.into(),
50 builder,
51 }
52 }
53
54 pub fn name(&self) -> &str {
56 &self.name
57 }
58
59 pub fn to_sql(&self) -> String {
61 format!("{} AS ({})", self.name, self.builder.to_sql())
62 }
63}
64
65pub struct WithQuery<T: ClickTable> {
67 _table: PhantomData<T>,
68 ctes: Vec<String>,
69}
70
71impl<T: ClickTable> WithQuery<T> {
72 pub fn new() -> Self {
74 Self {
75 _table: PhantomData,
76 ctes: Vec::new(),
77 }
78 }
79
80 pub fn with<T2: ClickTable>(
82 mut self,
83 cte: CTE<T2>,
84 ) -> Self {
85 self.ctes.push(cte.to_sql());
86 self
87 }
88
89 pub fn query(
91 self,
92 main_query: QueryBuilder<T>,
93 ) -> String {
94 let mut sql = String::from("WITH ");
95 sql.push_str(&self.ctes.join(", "));
96 sql.push(' ');
97 sql.push_str(&main_query.to_sql());
98 sql
99 }
100}
101
102impl<T: ClickTable> Default for WithQuery<T> {
103 fn default() -> Self {
104 Self::new()
105 }
106}
107
108pub struct InSubquery<T> {
110 column_name: String,
111 subquery_sql: String,
112 negated: bool,
113 _marker: PhantomData<T>,
114}
115
116impl<T> InSubquery<T> {
117 pub fn new(column_name: impl Into<String>, subquery_sql: impl Into<String>) -> Self {
119 Self {
120 column_name: column_name.into(),
121 subquery_sql: subquery_sql.into(),
122 negated: false,
123 _marker: PhantomData,
124 }
125 }
126
127 pub fn not_in(column_name: impl Into<String>, subquery_sql: impl Into<String>) -> Self {
129 Self {
130 column_name: column_name.into(),
131 subquery_sql: subquery_sql.into(),
132 negated: true,
133 _marker: PhantomData,
134 }
135 }
136
137 pub fn to_sql(&self) -> String {
139 if self.negated {
140 format!("{} NOT IN ({})", self.column_name, self.subquery_sql)
141 } else {
142 format!("{} IN ({})", self.column_name, self.subquery_sql)
143 }
144 }
145}
146
147pub struct ExistsSubquery {
149 subquery_sql: String,
150 negated: bool,
151}
152
153impl ExistsSubquery {
154 pub fn new(subquery_sql: impl Into<String>) -> Self {
156 Self {
157 subquery_sql: subquery_sql.into(),
158 negated: false,
159 }
160 }
161
162 pub fn not_exists(subquery_sql: impl Into<String>) -> Self {
164 Self {
165 subquery_sql: subquery_sql.into(),
166 negated: true,
167 }
168 }
169
170 pub fn to_sql(&self) -> String {
172 if self.negated {
173 format!("NOT EXISTS ({})", self.subquery_sql)
174 } else {
175 format!("EXISTS ({})", self.subquery_sql)
176 }
177 }
178}
179
180pub struct ScalarSubquery<R> {
182 subquery_sql: String,
183 _result_type: PhantomData<R>,
184}
185
186impl<R> ScalarSubquery<R> {
187 pub fn new(subquery_sql: impl Into<String>) -> Self {
189 Self {
190 subquery_sql: subquery_sql.into(),
191 _result_type: PhantomData,
192 }
193 }
194
195 pub fn to_sql(&self) -> String {
197 format!("({})", self.subquery_sql)
198 }
199
200 pub fn alias(self, alias: impl Into<String>) -> String {
202 format!("{} AS {}", self.to_sql(), alias.into())
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_subquery_basic() {
212 assert_eq!(1, 1); }
214
215 }