1use crate::expr::Expr;
4use sqlmodel_core::Value;
5
6#[derive(Debug, Clone)]
8pub struct Join {
9 pub join_type: JoinType,
11 pub table: String,
13 pub alias: Option<String>,
15 pub on: Expr,
17 pub lateral: bool,
21 pub is_subquery: bool,
23 pub subquery_params: Vec<Value>,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum JoinType {
30 Inner,
31 Left,
32 Right,
33 Full,
34 Cross,
35}
36
37impl JoinType {
38 pub const fn as_str(&self) -> &'static str {
40 match self {
41 JoinType::Inner => "INNER JOIN",
42 JoinType::Left => "LEFT JOIN",
43 JoinType::Right => "RIGHT JOIN",
44 JoinType::Full => "FULL JOIN",
45 JoinType::Cross => "CROSS JOIN",
46 }
47 }
48}
49
50impl Join {
51 pub fn inner(table: impl Into<String>, on: Expr) -> Self {
53 Self {
54 join_type: JoinType::Inner,
55 table: table.into(),
56 alias: None,
57 on,
58 lateral: false,
59 is_subquery: false,
60 subquery_params: Vec::new(),
61 }
62 }
63
64 pub fn left(table: impl Into<String>, on: Expr) -> Self {
66 Self {
67 join_type: JoinType::Left,
68 table: table.into(),
69 alias: None,
70 on,
71 lateral: false,
72 is_subquery: false,
73 subquery_params: Vec::new(),
74 }
75 }
76
77 pub fn right(table: impl Into<String>, on: Expr) -> Self {
79 Self {
80 join_type: JoinType::Right,
81 table: table.into(),
82 alias: None,
83 on,
84 lateral: false,
85 is_subquery: false,
86 subquery_params: Vec::new(),
87 }
88 }
89
90 pub fn full(table: impl Into<String>, on: Expr) -> Self {
92 Self {
93 join_type: JoinType::Full,
94 table: table.into(),
95 alias: None,
96 on,
97 lateral: false,
98 is_subquery: false,
99 subquery_params: Vec::new(),
100 }
101 }
102
103 pub fn cross(table: impl Into<String>) -> Self {
105 Self {
106 join_type: JoinType::Cross,
107 table: table.into(),
108 alias: None,
109 on: Expr::raw("TRUE"), lateral: false,
111 is_subquery: false,
112 subquery_params: Vec::new(),
113 }
114 }
115
116 pub fn lateral(
129 join_type: JoinType,
130 subquery_sql: impl Into<String>,
131 alias: impl Into<String>,
132 on: Expr,
133 params: Vec<Value>,
134 ) -> Self {
135 Self {
136 join_type,
137 table: subquery_sql.into(),
138 alias: Some(alias.into()),
139 on,
140 lateral: true,
141 is_subquery: true,
142 subquery_params: params,
143 }
144 }
145
146 pub fn left_lateral(
150 subquery_sql: impl Into<String>,
151 alias: impl Into<String>,
152 on: Expr,
153 params: Vec<Value>,
154 ) -> Self {
155 Self::lateral(JoinType::Left, subquery_sql, alias, on, params)
156 }
157
158 pub fn inner_lateral(
160 subquery_sql: impl Into<String>,
161 alias: impl Into<String>,
162 on: Expr,
163 params: Vec<Value>,
164 ) -> Self {
165 Self::lateral(JoinType::Inner, subquery_sql, alias, on, params)
166 }
167
168 pub fn cross_lateral(
170 subquery_sql: impl Into<String>,
171 alias: impl Into<String>,
172 params: Vec<Value>,
173 ) -> Self {
174 Self {
175 join_type: JoinType::Cross,
176 table: subquery_sql.into(),
177 alias: Some(alias.into()),
178 on: Expr::raw("TRUE"),
179 lateral: true,
180 is_subquery: true,
181 subquery_params: params,
182 }
183 }
184
185 pub fn alias(mut self, alias: impl Into<String>) -> Self {
187 self.alias = Some(alias.into());
188 self
189 }
190
191 pub fn set_lateral(mut self) -> Self {
193 self.lateral = true;
194 self
195 }
196
197 pub fn to_sql(&self) -> (String, Vec<Value>) {
202 let mut params = Vec::new();
203 let sql = self.build_sql(&mut params, 0);
204 (sql, params)
205 }
206
207 pub fn build(&self, params: &mut Vec<Value>, offset: usize) -> String {
209 self.build_sql(params, offset)
210 }
211
212 fn build_sql(&self, params: &mut Vec<Value>, offset: usize) -> String {
213 let lateral_keyword = if self.lateral { " LATERAL" } else { "" };
214
215 let table_ref = if self.is_subquery {
216 format!("({})", self.table)
217 } else {
218 self.table.clone()
219 };
220
221 let mut sql = format!(
222 " {}{}{}",
223 self.join_type.as_str(),
224 lateral_keyword,
225 if table_ref.is_empty() {
226 String::new()
227 } else {
228 format!(" {}", table_ref)
229 }
230 );
231
232 params.extend(self.subquery_params.clone());
234
235 if let Some(alias) = &self.alias {
236 sql.push_str(" AS ");
237 sql.push_str(alias);
238 }
239
240 if self.join_type != JoinType::Cross {
241 let on_sql = self.on.build(params, offset);
242 sql.push_str(" ON ");
243 sql.push_str(&on_sql);
244 }
245
246 sql
247 }
248}