sqlmo/query/select/
join.rs
1use crate::query::Where;
2use crate::util::SqlExtension;
3use crate::{Dialect, Select, ToSql};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub enum JoinTable {
7 Select(Select),
8 Table {
9 schema: Option<String>,
10 table: String,
11 },
12}
13
14#[derive(Debug, Copy, Clone, PartialEq, Eq)]
15pub enum JoinType {
16 Inner,
17 Left,
18 Right,
19 Full,
20}
21
22impl Default for JoinType {
23 fn default() -> Self {
24 JoinType::Inner
25 }
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub enum Criteria {
30 On(Where),
31 Using(Vec<String>),
32}
33
34impl From<Where> for Criteria {
35 fn from(where_: Where) -> Self {
36 Criteria::On(where_)
37 }
38}
39
40impl ToSql for Criteria {
41 fn write_sql(&self, buf: &mut String, dialect: Dialect) {
42 match self {
43 Criteria::On(where_) => {
44 buf.push_str(" ON ");
45 buf.push_sql(where_, dialect);
46 }
47 Criteria::Using(columns) => {
48 buf.push_str(" USING (");
49 buf.push_quoted_sequence(columns, ", ");
50 buf.push(')');
51 }
52 }
53 }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct Join {
58 pub typ: JoinType,
59 pub table: JoinTable,
60 pub alias: Option<String>,
61 pub criteria: Criteria,
62}
63
64impl Join {
65 pub fn new(table: &str) -> Self {
66 Self {
67 typ: JoinType::Inner,
68 table: JoinTable::Table {
69 schema: None,
70 table: table.to_string(),
71 },
72 alias: None,
73 criteria: Criteria::On(Where::And(vec![])),
74 }
75 }
76
77 pub fn left(table: &str) -> Self {
78 Self {
79 typ: JoinType::Left,
80 table: JoinTable::Table {
81 schema: None,
82 table: table.to_string(),
83 },
84 alias: None,
85 criteria: Criteria::On(Where::And(vec![])),
86 }
87 }
88
89 pub fn alias(mut self, alias: &str) -> Self {
90 self.alias = Some(alias.to_string());
91 self
92 }
93
94 pub fn on_raw(mut self, on: impl Into<String>) -> Self {
95 self.criteria = Criteria::On(Where::raw(on));
96 self
97 }
98}
99
100impl ToSql for Join {
101 fn write_sql(&self, buf: &mut String, dialect: Dialect) {
102 use JoinTable::*;
103 use JoinType::*;
104 match self.typ {
105 Inner => buf.push_str("JOIN "),
106 Left => buf.push_str("LEFT JOIN "),
107 Right => buf.push_str("RIGHT JOIN "),
108 Full => buf.push_str("FULL JOIN "),
109 }
110 match &self.table {
111 Select(s) => {
112 buf.push('(');
113 buf.push_str(&s.to_sql(dialect));
114 buf.push(')');
115 }
116 Table { schema, table } => {
117 buf.push_table_name(schema, table);
118 }
119 }
120 if let Some(alias) = &self.alias {
121 buf.push_str(" AS ");
122 buf.push_quoted(alias);
123 }
124 buf.push_sql(&self.criteria, dialect);
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn test_basic() {
134 let j = Join {
135 typ: JoinType::Inner,
136 table: JoinTable::Table {
137 schema: None,
138 table: "foo".to_string(),
139 },
140 alias: Some("bar".to_string()),
141 criteria: Criteria::On(Where::raw("bar.id = parent.bar_id".to_string())),
142 };
143 assert_eq!(
144 j.to_sql(Dialect::Postgres),
145 r#"JOIN "foo" AS "bar" ON bar.id = parent.bar_id"#
146 );
147 }
148
149 #[test]
150 fn test_builder() {
151 let j = Join::new("table")
152 .alias("bar")
153 .on_raw("bar.id = parent.bar_id");
154 assert_eq!(
155 j.to_sql(Dialect::Postgres),
156 r#"JOIN "table" AS "bar" ON bar.id = parent.bar_id"#
157 );
158 }
159}