sea_query/backend/
mod.rs

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