sea_query/backend/
mod.rs

1//! Translating the SQL AST into engine-specific SQL statements.
2
3use crate::*;
4use std::borrow::Cow;
5
6#[cfg(feature = "backend-mysql")]
7#[cfg_attr(docsrs, doc(cfg(feature = "backend-mysql")))]
8mod mysql;
9#[cfg(feature = "backend-postgres")]
10#[cfg_attr(docsrs, doc(cfg(feature = "backend-postgres")))]
11mod postgres;
12#[cfg(feature = "backend-sqlite")]
13#[cfg_attr(docsrs, doc(cfg(feature = "backend-sqlite")))]
14mod sqlite;
15
16#[cfg(feature = "backend-mysql")]
17pub use mysql::*;
18#[cfg(feature = "backend-postgres")]
19pub use postgres::*;
20#[cfg(feature = "backend-sqlite")]
21pub use sqlite::*;
22
23mod foreign_key_builder;
24mod index_builder;
25mod query_builder;
26mod table_builder;
27mod table_ref_builder;
28
29pub use self::foreign_key_builder::*;
30pub use self::index_builder::*;
31pub use self::query_builder::*;
32pub use self::table_builder::*;
33pub use self::table_ref_builder::*;
34
35pub trait GenericBuilder: QueryBuilder + SchemaBuilder {}
36
37pub trait SchemaBuilder: TableBuilder + IndexBuilder + ForeignKeyBuilder {}
38
39pub trait QuotedBuilder {
40    /// The type of quote the builder uses.
41    fn quote(&self) -> Quote;
42
43    /// To prepare iden and write to SQL.
44    fn prepare_iden(&self, iden: &DynIden, sql: &mut dyn SqlWriter) {
45        let q = self.quote();
46        let qq = q.1 as char;
47
48        sql.write_char(q.left()).unwrap();
49        match &iden.0 {
50            Cow::Borrowed(s) => sql.write_str(s).unwrap(),
51            Cow::Owned(s) => {
52                for char in s.chars() {
53                    if char == qq {
54                        sql.write_char(char).unwrap()
55                    }
56                    sql.write_char(char).unwrap()
57                }
58            }
59        };
60        sql.write_char(q.right()).unwrap();
61    }
62}
63
64pub trait EscapeBuilder {
65    /// Return if string literal needs to be escaped
66    fn need_escape(&self, s: &str) -> bool {
67        s.chars().any(|c| {
68            matches!(
69                c,
70                '\r' | '\n' | '\x1a' | '\x09' | '\x08' | '\0' | '\'' | '"' | '\\'
71            )
72        })
73    }
74
75    /// Escape a SQL string literal
76    fn escape_string(&self, string: &str) -> String {
77        let mut escaped = String::with_capacity(string.len() + 8);
78        self.write_escaped(&mut escaped, string);
79        escaped
80    }
81
82    fn write_escaped(&self, buffer: &mut dyn Write, string: &str) {
83        for c in string.chars() {
84            match c {
85                '\\' => buffer.write_str("\\\\"),
86                '"' => buffer.write_str("\\\""),
87                '\'' => buffer.write_str("\\'"),
88                '\0' => buffer.write_str("\\0"),
89                '\x08' => buffer.write_str("\\b"),
90                '\x09' => buffer.write_str("\\t"),
91                '\x1a' => buffer.write_str("\\z"),
92                '\n' => buffer.write_str("\\n"),
93                '\r' => buffer.write_str("\\r"),
94                _ => buffer.write_char(c),
95            }
96            .unwrap()
97        }
98    }
99
100    /// Unescape a SQL string literal
101    fn unescape_string(&self, string: &str) -> String {
102        let mut escape = false;
103        let mut output = String::new();
104        for c in string.chars() {
105            if !escape && c == '\\' {
106                escape = true;
107            } else if escape {
108                output
109                    .write_char(match c {
110                        '0' => '\0',
111                        'b' => '\x08',
112                        't' => '\x09',
113                        'z' => '\x1a',
114                        'n' => '\n',
115                        'r' => '\r',
116                        c => c,
117                    })
118                    .unwrap();
119                escape = false;
120            } else {
121                output.write_char(c).unwrap();
122            }
123        }
124        output
125    }
126}
127
128pub trait PrecedenceDecider {
129    // This method decides which precedence relations should lead to dropped parentheses.
130    // There will be more fine grained precedence relations than the ones represented here,
131    // but dropping parentheses due to these relations can be confusing for readers.
132    fn inner_expr_well_known_greater_precedence(&self, inner: &Expr, outer_oper: &Oper) -> bool;
133}
134
135pub trait OperLeftAssocDecider {
136    // This method decides if the left associativity of an operator should lead to dropped parentheses.
137    // Not all known left associative operators are necessarily included here,
138    // as dropping them may in some cases be confusing to readers.
139    fn well_known_left_associative(&self, op: &BinOper) -> bool;
140}
141
142#[derive(Debug, PartialEq)]
143pub enum Oper {
144    UnOper(UnOper),
145    BinOper(BinOper),
146}
147
148impl From<UnOper> for Oper {
149    fn from(value: UnOper) -> Self {
150        Oper::UnOper(value)
151    }
152}
153
154impl From<BinOper> for Oper {
155    fn from(value: BinOper) -> Self {
156        Oper::BinOper(value)
157    }
158}
159
160impl Oper {
161    pub(crate) fn is_logical(&self) -> bool {
162        matches!(
163            self,
164            Oper::UnOper(UnOper::Not) | Oper::BinOper(BinOper::And) | Oper::BinOper(BinOper::Or)
165        )
166    }
167
168    pub(crate) fn is_between(&self) -> bool {
169        matches!(
170            self,
171            Oper::BinOper(BinOper::Between) | Oper::BinOper(BinOper::NotBetween)
172        )
173    }
174
175    pub(crate) fn is_like(&self) -> bool {
176        matches!(
177            self,
178            Oper::BinOper(BinOper::Like) | Oper::BinOper(BinOper::NotLike)
179        )
180    }
181
182    pub(crate) fn is_in(&self) -> bool {
183        matches!(
184            self,
185            Oper::BinOper(BinOper::In) | Oper::BinOper(BinOper::NotIn)
186        )
187    }
188
189    pub(crate) fn is_is(&self) -> bool {
190        matches!(
191            self,
192            Oper::BinOper(BinOper::Is) | Oper::BinOper(BinOper::IsNot)
193        )
194    }
195
196    pub(crate) fn is_shift(&self) -> bool {
197        matches!(
198            self,
199            Oper::BinOper(BinOper::LShift) | Oper::BinOper(BinOper::RShift)
200        )
201    }
202
203    pub(crate) fn is_arithmetic(&self) -> bool {
204        match self {
205            Oper::BinOper(b) => {
206                matches!(
207                    b,
208                    BinOper::Mul | BinOper::Div | BinOper::Mod | BinOper::Add | BinOper::Sub
209                )
210            }
211            _ => false,
212        }
213    }
214
215    pub(crate) fn is_comparison(&self) -> bool {
216        match self {
217            Oper::BinOper(b) => {
218                matches!(
219                    b,
220                    BinOper::SmallerThan
221                        | BinOper::SmallerThanOrEqual
222                        | BinOper::Equal
223                        | BinOper::GreaterThanOrEqual
224                        | BinOper::GreaterThan
225                        | BinOper::NotEqual
226                )
227            }
228            _ => false,
229        }
230    }
231}