1use crate::{SQL, ToSQL, traits::SQLParam};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
14pub enum JoinType {
15 #[default]
16 Join,
17 Inner,
18 Left,
19 Right,
20 Full,
21 Cross,
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
33pub struct Join {
34 pub natural: bool,
35 pub join_type: JoinType,
36 pub outer: bool, }
38
39impl Join {
40 #[must_use]
42 pub const fn new() -> Self {
43 Self {
44 natural: false,
45 join_type: JoinType::Join,
46 outer: false,
47 }
48 }
49
50 #[must_use]
52 pub const fn natural(mut self) -> Self {
53 self.natural = true;
54 self
55 }
56
57 #[must_use]
59 pub const fn inner(mut self) -> Self {
60 self.join_type = JoinType::Inner;
61 self
62 }
63
64 #[must_use]
66 pub const fn left(mut self) -> Self {
67 self.join_type = JoinType::Left;
68 self
69 }
70
71 #[must_use]
73 pub const fn right(mut self) -> Self {
74 self.join_type = JoinType::Right;
75 self
76 }
77
78 #[must_use]
80 pub const fn full(mut self) -> Self {
81 self.join_type = JoinType::Full;
82 self
83 }
84
85 #[must_use]
87 pub const fn cross(mut self) -> Self {
88 self.join_type = JoinType::Cross;
89 self
90 }
91
92 #[must_use]
94 pub const fn outer(mut self) -> Self {
95 self.outer = true;
96 self
97 }
98}
99
100impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for Join {
101 fn to_sql(&self) -> SQL<'a, V> {
102 let join_str = match (self.natural, self.join_type, self.outer) {
104 (true, JoinType::Join, _) => "NATURAL JOIN",
106 (true, JoinType::Inner, _) => "NATURAL INNER JOIN",
107 (true, JoinType::Left, false) => "NATURAL LEFT JOIN",
108 (true, JoinType::Left, true) => "NATURAL LEFT OUTER JOIN",
109 (true, JoinType::Right, false) => "NATURAL RIGHT JOIN",
110 (true, JoinType::Right, true) => "NATURAL RIGHT OUTER JOIN",
111 (true, JoinType::Full, false) => "NATURAL FULL JOIN",
112 (true, JoinType::Full, true) => "NATURAL FULL OUTER JOIN",
113 (true, JoinType::Cross, _) => "NATURAL CROSS JOIN",
114 (false, JoinType::Join, _) => "JOIN",
116 (false, JoinType::Inner, _) => "INNER JOIN",
117 (false, JoinType::Left, false) => "LEFT JOIN",
118 (false, JoinType::Left, true) => "LEFT OUTER JOIN",
119 (false, JoinType::Right, false) => "RIGHT JOIN",
120 (false, JoinType::Right, true) => "RIGHT OUTER JOIN",
121 (false, JoinType::Full, false) => "FULL JOIN",
122 (false, JoinType::Full, true) => "FULL OUTER JOIN",
123 (false, JoinType::Cross, _) => "CROSS JOIN",
124 };
125 SQL::raw(join_str)
126 }
127}
128
129#[macro_export]
153macro_rules! impl_join_helpers {
154 (
155 table_trait: $TableTrait:path,
156 condition_trait: $ConditionTrait:path,
157 sql_type: $SQLType:ty $(,)?
158 ) => {
159 fn join_internal<'a, Table>(
160 table: Table,
161 join: $crate::Join,
162 condition: impl $ConditionTrait,
163 ) -> $SQLType
164 where
165 Table: $TableTrait,
166 {
167 use $crate::ToSQL;
168 join.to_sql()
169 .append(&table)
170 .push($crate::Token::ON)
171 .append(&condition)
172 }
173
174 pub fn natural_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
176 where
177 Table: $TableTrait,
178 {
179 join_internal(table, $crate::Join::new().natural(), condition)
180 }
181
182 pub fn join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
184 where
185 Table: $TableTrait,
186 {
187 join_internal(table, $crate::Join::new(), condition)
188 }
189
190 pub fn natural_left_join<'a, Table>(
192 table: Table,
193 condition: impl $ConditionTrait,
194 ) -> $SQLType
195 where
196 Table: $TableTrait,
197 {
198 join_internal(table, $crate::Join::new().natural().left(), condition)
199 }
200
201 pub fn left_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
203 where
204 Table: $TableTrait,
205 {
206 join_internal(table, $crate::Join::new().left(), condition)
207 }
208
209 pub fn left_outer_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
211 where
212 Table: $TableTrait,
213 {
214 join_internal(table, $crate::Join::new().left().outer(), condition)
215 }
216
217 pub fn natural_left_outer_join<'a, Table>(
219 table: Table,
220 condition: impl $ConditionTrait,
221 ) -> $SQLType
222 where
223 Table: $TableTrait,
224 {
225 join_internal(
226 table,
227 $crate::Join::new().natural().left().outer(),
228 condition,
229 )
230 }
231
232 pub fn natural_right_join<'a, Table>(
234 table: Table,
235 condition: impl $ConditionTrait,
236 ) -> $SQLType
237 where
238 Table: $TableTrait,
239 {
240 join_internal(table, $crate::Join::new().natural().right(), condition)
241 }
242
243 pub fn right_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
245 where
246 Table: $TableTrait,
247 {
248 join_internal(table, $crate::Join::new().right(), condition)
249 }
250
251 pub fn right_outer_join<'a, Table>(
253 table: Table,
254 condition: impl $ConditionTrait,
255 ) -> $SQLType
256 where
257 Table: $TableTrait,
258 {
259 join_internal(table, $crate::Join::new().right().outer(), condition)
260 }
261
262 pub fn natural_right_outer_join<'a, Table>(
264 table: Table,
265 condition: impl $ConditionTrait,
266 ) -> $SQLType
267 where
268 Table: $TableTrait,
269 {
270 join_internal(
271 table,
272 $crate::Join::new().natural().right().outer(),
273 condition,
274 )
275 }
276
277 pub fn natural_full_join<'a, Table>(
279 table: Table,
280 condition: impl $ConditionTrait,
281 ) -> $SQLType
282 where
283 Table: $TableTrait,
284 {
285 join_internal(table, $crate::Join::new().natural().full(), condition)
286 }
287
288 pub fn full_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
290 where
291 Table: $TableTrait,
292 {
293 join_internal(table, $crate::Join::new().full(), condition)
294 }
295
296 pub fn full_outer_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
298 where
299 Table: $TableTrait,
300 {
301 join_internal(table, $crate::Join::new().full().outer(), condition)
302 }
303
304 pub fn natural_full_outer_join<'a, Table>(
306 table: Table,
307 condition: impl $ConditionTrait,
308 ) -> $SQLType
309 where
310 Table: $TableTrait,
311 {
312 join_internal(
313 table,
314 $crate::Join::new().natural().full().outer(),
315 condition,
316 )
317 }
318
319 pub fn natural_inner_join<'a, Table>(
321 table: Table,
322 condition: impl $ConditionTrait,
323 ) -> $SQLType
324 where
325 Table: $TableTrait,
326 {
327 join_internal(table, $crate::Join::new().natural().inner(), condition)
328 }
329
330 pub fn inner_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
332 where
333 Table: $TableTrait,
334 {
335 join_internal(table, $crate::Join::new().inner(), condition)
336 }
337
338 pub fn cross_join<'a, Table>(table: Table, condition: impl $ConditionTrait) -> $SQLType
340 where
341 Table: $TableTrait,
342 {
343 join_internal(table, $crate::Join::new().cross(), condition)
344 }
345 };
346}
347
348#[macro_export]
354macro_rules! impl_join_arg_trait {
355 (
356 table_trait: $TableTrait:path,
357 table_info_trait: $TableInfoTrait:path,
358 condition_trait: $ConditionTrait:path,
359 value_type: $ValueType:ty $(,)?
360 ) => {
361 pub trait JoinArg<'a, FromTable> {
363 type JoinedTable;
364 fn into_join_sql(self, join: $crate::Join) -> $crate::SQL<'a, $ValueType>;
365 }
366
367 impl<'a, U, T> JoinArg<'a, T> for U
369 where
370 U: $TableTrait + $crate::Joinable<T>,
371 T: $TableInfoTrait + ::core::default::Default,
372 {
373 type JoinedTable = U;
374
375 fn into_join_sql(self, join: $crate::Join) -> $crate::SQL<'a, $ValueType> {
376 use $crate::ToSQL;
377
378 let from = T::default();
379 let cols = <U as $crate::Joinable<T>>::fk_columns();
380 let join_name = self.name();
381 let from_name = from.name();
382
383 let mut condition = $crate::SQL::with_capacity_chunks(cols.len() * 7);
384 for (idx, (self_col, target_col)) in cols.iter().enumerate() {
385 if idx > 0 {
386 condition.push_mut($crate::Token::AND);
387 }
388 condition.append_mut(
389 $crate::SQL::ident(join_name)
390 .push($crate::Token::DOT)
391 .append($crate::SQL::ident(*self_col)),
392 );
393 condition.push_mut($crate::Token::EQ);
394 condition.append_mut(
395 $crate::SQL::ident(from_name)
396 .push($crate::Token::DOT)
397 .append($crate::SQL::ident(*target_col)),
398 );
399 }
400
401 join.to_sql()
402 .append(&self)
403 .push($crate::Token::ON)
404 .append(&condition)
405 }
406 }
407
408 impl<'a, U, C, T> JoinArg<'a, T> for (U, C)
410 where
411 U: $TableTrait,
412 C: $ConditionTrait,
413 {
414 type JoinedTable = U;
415
416 fn into_join_sql(self, join: $crate::Join) -> $crate::SQL<'a, $ValueType> {
417 let (table, condition) = self;
418 join_internal(table, join, condition)
419 }
420 }
421 };
422}