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}