1use sqlx::FromRow;
7
8use crate::Value;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum Driver {
13 #[cfg(feature = "mysql")]
15 MySql,
16 #[cfg(feature = "postgres")]
18 Postgres,
19 #[cfg(feature = "sqlite")]
21 Sqlite,
22}
23
24impl Driver {
25 pub fn name(self) -> &'static str {
27 match self {
28 #[cfg(feature = "mysql")]
29 Self::MySql => "MySQL",
30 #[cfg(feature = "postgres")]
31 Self::Postgres => "PostgreSQL",
32 #[cfg(feature = "sqlite")]
33 Self::Sqlite => "SQLite",
34 }
35 }
36
37 pub(crate) fn quote(self, name: &str) -> String {
40 match self {
41 #[cfg(feature = "mysql")]
42 Self::MySql => format!("`{name}`"),
43 #[cfg(any(feature = "postgres", feature = "sqlite"))]
44 _ => format!("\"{name}\""),
45 }
46 }
47
48 #[cfg_attr(not(feature = "postgres"), allow(unused_variables))]
51 pub(crate) fn placeholder(self, n: usize) -> String {
52 match self {
53 #[cfg(feature = "mysql")]
54 Self::MySql => "?".to_owned(),
55 #[cfg(feature = "postgres")]
56 Self::Postgres => format!("${n}"),
57 #[cfg(feature = "sqlite")]
58 Self::Sqlite => "?".to_owned(),
59 }
60 }
61
62 pub(crate) fn lock_clause(self) -> &'static str {
66 match self {
67 #[cfg(feature = "mysql")]
68 Self::MySql => " FOR UPDATE",
69 #[cfg(feature = "postgres")]
70 Self::Postgres => " FOR UPDATE",
71 #[cfg(feature = "sqlite")]
72 Self::Sqlite => "",
73 }
74 }
75
76 pub(crate) fn offset_only_limit(self) -> Option<&'static str> {
79 match self {
80 #[cfg(feature = "mysql")]
81 Self::MySql => Some("18446744073709551615"),
82 #[cfg(feature = "postgres")]
83 Self::Postgres => None,
84 #[cfg(feature = "sqlite")]
85 Self::Sqlite => Some("-1"),
86 }
87 }
88
89 pub(crate) fn empty_insert(self) -> &'static str {
91 match self {
92 #[cfg(feature = "mysql")]
93 Self::MySql => "() VALUES ()",
94 #[cfg(any(feature = "postgres", feature = "sqlite"))]
95 _ => "DEFAULT VALUES",
96 }
97 }
98
99 pub(crate) fn bigint(self) -> &'static str {
101 match self {
102 #[cfg(feature = "mysql")]
103 Self::MySql => "SIGNED",
104 #[cfg(feature = "postgres")]
105 Self::Postgres => "BIGINT",
106 #[cfg(feature = "sqlite")]
107 Self::Sqlite => "INTEGER",
108 }
109 }
110
111 pub(crate) fn supports_replace(self) -> bool {
114 match self {
115 #[cfg(feature = "mysql")]
116 Self::MySql => true,
117 #[cfg(feature = "postgres")]
118 Self::Postgres => false,
119 #[cfg(feature = "sqlite")]
120 Self::Sqlite => true,
121 }
122 }
123
124 pub(crate) fn inlines_null(self) -> bool {
127 match self {
128 #[cfg(feature = "mysql")]
129 Self::MySql => false,
130 #[cfg(feature = "postgres")]
131 Self::Postgres => true,
132 #[cfg(feature = "sqlite")]
133 Self::Sqlite => false,
134 }
135 }
136
137 pub(crate) fn insert_returns_id(self) -> bool {
139 match self {
140 #[cfg(feature = "mysql")]
141 Self::MySql => false,
142 #[cfg(feature = "postgres")]
143 Self::Postgres => true,
144 #[cfg(feature = "sqlite")]
145 Self::Sqlite => false,
146 }
147 }
148
149 pub(crate) fn prepare(self, sql: &str, binds: &[Value]) -> (String, Vec<Value>) {
153 match self {
154 #[cfg(feature = "mysql")]
155 Self::MySql => (sql.to_owned(), binds.to_vec()),
156 #[cfg(feature = "postgres")]
157 Self::Postgres => prepare_postgres(sql, binds),
158 #[cfg(feature = "sqlite")]
159 Self::Sqlite => (sql.to_owned(), binds.to_vec()),
160 }
161 }
162}
163
164#[cfg(feature = "postgres")]
167fn prepare_postgres(sql: &str, binds: &[Value]) -> (String, Vec<Value>) {
168 let mut out = String::with_capacity(sql.len() + 8);
169 let mut kept = Vec::new();
170 let mut seen = 0usize;
171 let mut chars = sql.chars().peekable();
172 let mut quote: Option<char> = None;
173 while let Some(c) = chars.next() {
174 match quote {
175 Some(q) => {
176 out.push(c);
177 if c == q {
178 quote = None;
179 }
180 }
181 None => match c {
182 '\'' | '"' => {
183 quote = Some(c);
184 out.push(c);
185 }
186 '-' if chars.peek() == Some(&'-') => {
187 out.push(c);
188 for next in chars.by_ref() {
189 out.push(next);
190 if next == '\n' {
191 break;
192 }
193 }
194 }
195 '?' => {
196 let value = binds.get(seen).cloned().unwrap_or(Value::Null);
197 seen += 1;
198 if matches!(value, Value::Null) {
199 out.push_str("NULL");
200 } else {
201 kept.push(value);
202 out.push_str(&format!("${}", kept.len()));
203 }
204 }
205 _ => out.push(c),
206 },
207 }
208 }
209 (out, kept)
210}
211
212#[doc(hidden)]
216pub mod rows {
217 #[cfg(feature = "mysql")]
218 pub trait RowMySql: for<'r> super::FromRow<'r, sqlx::mysql::MySqlRow> {}
219 #[cfg(feature = "mysql")]
220 impl<T: for<'r> super::FromRow<'r, sqlx::mysql::MySqlRow>> RowMySql for T {}
221 #[cfg(not(feature = "mysql"))]
222 pub trait RowMySql {}
223 #[cfg(not(feature = "mysql"))]
224 impl<T> RowMySql for T {}
225
226 #[cfg(feature = "postgres")]
227 pub trait RowPostgres: for<'r> super::FromRow<'r, sqlx::postgres::PgRow> {}
228 #[cfg(feature = "postgres")]
229 impl<T: for<'r> super::FromRow<'r, sqlx::postgres::PgRow>> RowPostgres for T {}
230 #[cfg(not(feature = "postgres"))]
231 pub trait RowPostgres {}
232 #[cfg(not(feature = "postgres"))]
233 impl<T> RowPostgres for T {}
234
235 #[cfg(feature = "sqlite")]
236 pub trait RowSqlite: for<'r> super::FromRow<'r, sqlx::sqlite::SqliteRow> {}
237 #[cfg(feature = "sqlite")]
238 impl<T: for<'r> super::FromRow<'r, sqlx::sqlite::SqliteRow>> RowSqlite for T {}
239 #[cfg(not(feature = "sqlite"))]
240 pub trait RowSqlite {}
241 #[cfg(not(feature = "sqlite"))]
242 impl<T> RowSqlite for T {}
243
244 pub trait DriverRows: RowMySql + RowPostgres + RowSqlite {}
246 impl<T: RowMySql + RowPostgres + RowSqlite> DriverRows for T {}
247}
248
249#[cfg(not(any(feature = "mysql", feature = "postgres", feature = "sqlite")))]
250compile_error!("orbit-orm needs at least one database feature: mysql, postgres or sqlite");