sea_query/backend/
mod.rs

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