1use crate::config::DatabaseBackend;
4use crate::error::{SqlxError, SqlxResult};
5use serde_json::Value as JsonValue;
6use sqlx::Row;
7
8pub enum SqlxRow {
10 #[cfg(feature = "postgres")]
12 Postgres(sqlx::postgres::PgRow),
13 #[cfg(feature = "mysql")]
15 MySql(sqlx::mysql::MySqlRow),
16 #[cfg(feature = "sqlite")]
18 Sqlite(sqlx::sqlite::SqliteRow),
19}
20
21impl SqlxRow {
22 pub fn backend(&self) -> DatabaseBackend {
24 match self {
25 #[cfg(feature = "postgres")]
26 Self::Postgres(_) => DatabaseBackend::Postgres,
27 #[cfg(feature = "mysql")]
28 Self::MySql(_) => DatabaseBackend::MySql,
29 #[cfg(feature = "sqlite")]
30 Self::Sqlite(_) => DatabaseBackend::Sqlite,
31 }
32 }
33
34 pub fn get<T>(&self, index: usize) -> SqlxResult<T>
36 where
37 T: SqlxDecode,
38 {
39 T::decode_from_row(self, index)
40 }
41
42 pub fn get_by_name<T>(&self, name: &str) -> SqlxResult<T>
44 where
45 T: SqlxDecodeNamed,
46 {
47 T::decode_by_name(self, name)
48 }
49
50 pub fn try_get<T>(&self, index: usize) -> SqlxResult<Option<T>>
52 where
53 T: SqlxDecode,
54 {
55 match T::decode_from_row(self, index) {
56 Ok(v) => Ok(Some(v)),
57 Err(SqlxError::Sqlx(sqlx::Error::ColumnNotFound(_))) => Ok(None),
58 Err(SqlxError::Sqlx(sqlx::Error::ColumnDecode { ref source, .. }))
62 if source.is::<sqlx::error::UnexpectedNullError>() =>
63 {
64 Ok(None)
65 }
66 Err(e) => Err(e),
67 }
68 }
69
70 pub fn len(&self) -> usize {
72 match self {
73 #[cfg(feature = "postgres")]
74 Self::Postgres(row) => row.len(),
75 #[cfg(feature = "mysql")]
76 Self::MySql(row) => row.len(),
77 #[cfg(feature = "sqlite")]
78 Self::Sqlite(row) => row.len(),
79 }
80 }
81
82 pub fn is_empty(&self) -> bool {
84 self.len() == 0
85 }
86
87 pub fn to_json(&self) -> SqlxResult<JsonValue> {
89 match self {
90 #[cfg(feature = "postgres")]
91 Self::Postgres(row) => row_to_json_pg(row),
92 #[cfg(feature = "mysql")]
93 Self::MySql(row) => row_to_json_mysql(row),
94 #[cfg(feature = "sqlite")]
95 Self::Sqlite(row) => row_to_json_sqlite(row),
96 }
97 }
98}
99
100pub trait SqlxDecode: Sized {
102 fn decode_from_row(row: &SqlxRow, index: usize) -> SqlxResult<Self>;
104}
105
106pub trait SqlxDecodeNamed: Sized {
108 fn decode_by_name(row: &SqlxRow, name: &str) -> SqlxResult<Self>;
110}
111
112macro_rules! impl_decode {
114 ($ty:ty) => {
115 impl SqlxDecode for $ty {
116 fn decode_from_row(row: &SqlxRow, index: usize) -> SqlxResult<Self> {
117 match row {
118 #[cfg(feature = "postgres")]
119 SqlxRow::Postgres(r) => r.try_get(index).map_err(SqlxError::from),
120 #[cfg(feature = "mysql")]
121 SqlxRow::MySql(r) => r.try_get(index).map_err(SqlxError::from),
122 #[cfg(feature = "sqlite")]
123 SqlxRow::Sqlite(r) => r.try_get(index).map_err(SqlxError::from),
124 }
125 }
126 }
127
128 impl SqlxDecodeNamed for $ty {
129 fn decode_by_name(row: &SqlxRow, name: &str) -> SqlxResult<Self> {
130 match row {
131 #[cfg(feature = "postgres")]
132 SqlxRow::Postgres(r) => r.try_get(name).map_err(SqlxError::from),
133 #[cfg(feature = "mysql")]
134 SqlxRow::MySql(r) => r.try_get(name).map_err(SqlxError::from),
135 #[cfg(feature = "sqlite")]
136 SqlxRow::Sqlite(r) => r.try_get(name).map_err(SqlxError::from),
137 }
138 }
139 }
140 };
141}
142
143impl_decode!(i32);
144impl_decode!(i64);
145impl_decode!(f32);
146impl_decode!(f64);
147impl_decode!(bool);
148impl_decode!(String);
149impl_decode!(Vec<u8>);
150
151fn f64_to_json(f: f64) -> JsonValue {
163 serde_json::Number::from_f64(f).map_or(JsonValue::Null, JsonValue::Number)
164}
165
166#[cfg(any(feature = "postgres", feature = "mysql"))]
168fn decimal_to_json(d: rust_decimal::Decimal) -> JsonValue {
169 serde_json::to_value(d).unwrap_or(JsonValue::Null)
170}
171
172fn bytes_to_json(bytes: Vec<u8>) -> JsonValue {
174 use prax_query::base64::Engine as _;
175 JsonValue::String(prax_query::base64::engine::general_purpose::STANDARD.encode(bytes))
176}
177
178#[cfg(feature = "postgres")]
179fn row_to_json_pg(row: &sqlx::postgres::PgRow) -> SqlxResult<JsonValue> {
180 use sqlx::Column;
181 let mut obj = serde_json::Map::new();
182 for (i, col) in row.columns().iter().enumerate() {
183 let name = col.name().to_string();
184 obj.insert(name, pg_cell_to_json(row, i));
185 }
186 Ok(JsonValue::Object(obj))
187}
188
189#[cfg(feature = "postgres")]
190fn pg_cell_to_json(row: &sqlx::postgres::PgRow, i: usize) -> JsonValue {
191 if let Ok(Some(s)) = row.try_get::<Option<String>, _>(i) {
192 return JsonValue::String(s);
193 }
194 if let Ok(Some(b)) = row.try_get::<Option<bool>, _>(i) {
195 return JsonValue::Bool(b);
196 }
197 if let Ok(Some(n)) = row.try_get::<Option<i64>, _>(i) {
198 return JsonValue::Number(n.into());
199 }
200 if let Ok(Some(n)) = row.try_get::<Option<i32>, _>(i) {
201 return JsonValue::Number(n.into());
202 }
203 if let Ok(Some(n)) = row.try_get::<Option<i16>, _>(i) {
204 return JsonValue::Number(n.into());
205 }
206 if let Ok(Some(f)) = row.try_get::<Option<f64>, _>(i) {
207 return f64_to_json(f);
208 }
209 if let Ok(Some(f)) = row.try_get::<Option<f32>, _>(i) {
210 return f64_to_json(f64::from(f));
211 }
212 if let Ok(Some(d)) = row.try_get::<Option<rust_decimal::Decimal>, _>(i) {
213 return decimal_to_json(d);
214 }
215 if let Ok(Some(ts)) = row.try_get::<Option<chrono::DateTime<chrono::Utc>>, _>(i) {
216 return JsonValue::String(ts.to_rfc3339());
217 }
218 if let Ok(Some(ts)) = row.try_get::<Option<chrono::NaiveDateTime>, _>(i) {
219 return JsonValue::String(ts.to_string());
220 }
221 if let Ok(Some(d)) = row.try_get::<Option<chrono::NaiveDate>, _>(i) {
222 return JsonValue::String(d.to_string());
223 }
224 if let Ok(Some(t)) = row.try_get::<Option<chrono::NaiveTime>, _>(i) {
225 return JsonValue::String(t.to_string());
226 }
227 if let Ok(Some(u)) = row.try_get::<Option<uuid::Uuid>, _>(i) {
228 return JsonValue::String(u.to_string());
229 }
230 if let Ok(Some(j)) = row.try_get::<Option<JsonValue>, _>(i) {
231 return j;
232 }
233 if let Ok(Some(b)) = row.try_get::<Option<Vec<u8>>, _>(i) {
234 return bytes_to_json(b);
235 }
236 JsonValue::Null
237}
238
239#[cfg(feature = "mysql")]
240fn row_to_json_mysql(row: &sqlx::mysql::MySqlRow) -> SqlxResult<JsonValue> {
241 use sqlx::Column;
242 let mut obj = serde_json::Map::new();
243 for (i, col) in row.columns().iter().enumerate() {
244 let name = col.name().to_string();
245 obj.insert(name, mysql_cell_to_json(row, i));
246 }
247 Ok(JsonValue::Object(obj))
248}
249
250#[cfg(feature = "mysql")]
251fn mysql_cell_to_json(row: &sqlx::mysql::MySqlRow, i: usize) -> JsonValue {
252 if let Ok(Some(s)) = row.try_get::<Option<String>, _>(i) {
253 return JsonValue::String(s);
254 }
255 if let Ok(Some(b)) = row.try_get::<Option<bool>, _>(i) {
256 return JsonValue::Bool(b);
257 }
258 if let Ok(Some(n)) = row.try_get::<Option<i64>, _>(i) {
259 return JsonValue::Number(n.into());
260 }
261 if let Ok(Some(f)) = row.try_get::<Option<f64>, _>(i) {
262 return f64_to_json(f);
263 }
264 if let Ok(Some(d)) = row.try_get::<Option<rust_decimal::Decimal>, _>(i) {
265 return decimal_to_json(d);
266 }
267 if let Ok(Some(ts)) = row.try_get::<Option<chrono::DateTime<chrono::Utc>>, _>(i) {
268 return JsonValue::String(ts.to_rfc3339());
269 }
270 if let Ok(Some(ts)) = row.try_get::<Option<chrono::NaiveDateTime>, _>(i) {
271 return JsonValue::String(ts.to_string());
272 }
273 if let Ok(Some(d)) = row.try_get::<Option<chrono::NaiveDate>, _>(i) {
274 return JsonValue::String(d.to_string());
275 }
276 if let Ok(Some(t)) = row.try_get::<Option<chrono::NaiveTime>, _>(i) {
277 return JsonValue::String(t.to_string());
278 }
279 if let Ok(Some(j)) = row.try_get::<Option<JsonValue>, _>(i) {
280 return j;
281 }
282 if let Ok(Some(b)) = row.try_get::<Option<Vec<u8>>, _>(i) {
283 return bytes_to_json(b);
284 }
285 JsonValue::Null
286}
287
288#[cfg(feature = "sqlite")]
289fn row_to_json_sqlite(row: &sqlx::sqlite::SqliteRow) -> SqlxResult<JsonValue> {
290 use sqlx::Column;
291 let mut obj = serde_json::Map::new();
292 for (i, col) in row.columns().iter().enumerate() {
293 let name = col.name().to_string();
294 obj.insert(name, sqlite_cell_to_json(row, i));
295 }
296 Ok(JsonValue::Object(obj))
297}
298
299#[cfg(feature = "sqlite")]
300fn sqlite_cell_to_json(row: &sqlx::sqlite::SqliteRow, i: usize) -> JsonValue {
301 if let Ok(Some(s)) = row.try_get::<Option<String>, _>(i) {
305 return JsonValue::String(s);
306 }
307 if let Ok(Some(n)) = row.try_get::<Option<i64>, _>(i) {
308 return JsonValue::Number(n.into());
309 }
310 if let Ok(Some(f)) = row.try_get::<Option<f64>, _>(i) {
311 return f64_to_json(f);
312 }
313 if let Ok(Some(b)) = row.try_get::<Option<bool>, _>(i) {
314 return JsonValue::Bool(b);
315 }
316 if let Ok(Some(b)) = row.try_get::<Option<Vec<u8>>, _>(i) {
317 return bytes_to_json(b);
318 }
319 JsonValue::Null
320}
321
322pub trait FromSqlxRow: Sized {
324 fn from_row(row: SqlxRow) -> SqlxResult<Self>;
326}