1use bigdecimal::BigDecimal;
12use sqlx::mysql::MySqlRow;
13use sqlx::types::chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc};
14
15use crate::numeric::bigdecimal_to_json;
16use crate::prelude::*;
17
18impl RowExt for MySqlRow {
19 fn to_json(&self) -> Value {
20 let columns = self.columns();
21 let mut map = Map::with_capacity(columns.len());
22
23 for column in columns {
24 let idx = column.ordinal();
25 let type_name = column.type_info().name();
26
27 let value = if self.try_get_raw(idx).is_ok_and(|v| v.is_null()) {
28 Value::Null
29 } else {
30 match type_name {
31 "BOOLEAN" => self.try_get::<bool, _>(idx).map_or(Value::Null, Value::Bool),
32
33 "TINYINT" | "SMALLINT" | "INT" | "MEDIUMINT" | "BIGINT" | "TINYINT UNSIGNED"
34 | "SMALLINT UNSIGNED" | "INT UNSIGNED" | "MEDIUMINT UNSIGNED" | "YEAR" => self
35 .try_get::<i64, _>(idx)
36 .map_or(Value::Null, |v| Value::Number(v.into())),
37
38 "BIGINT UNSIGNED" => self.try_get::<u64, _>(idx).map_or(Value::Null, |v| {
39 i64::try_from(v)
40 .map_or_else(|_| Value::String(v.to_string()), |signed| Value::Number(signed.into()))
41 }),
42
43 "DECIMAL" => self
44 .try_get::<BigDecimal, _>(idx)
45 .map_or(Value::Null, |v| bigdecimal_to_json(&v)),
46
47 "FLOAT" => self.try_get::<f32, _>(idx).map_or(Value::Null, Value::from),
48
49 "DOUBLE" => self.try_get::<f64, _>(idx).map_or(Value::Null, Value::from),
50
51 "JSON" => self.try_get::<Value, _>(idx).unwrap_or(Value::Null),
52
53 "BINARY" | "VARBINARY" => self
56 .try_get::<String, _>(idx)
57 .map_or_else(|_| bytes_to_json(self, idx), Value::String),
58
59 "BLOB" | "TINYBLOB" | "MEDIUMBLOB" | "LONGBLOB" | "BIT" | "GEOMETRY" => bytes_to_json(self, idx),
60
61 "DATE" => self
62 .try_get::<NaiveDate, _>(idx)
63 .map_or(Value::Null, |v| Value::String(v.to_string())),
64
65 "TIME" => self
66 .try_get::<NaiveTime, _>(idx)
67 .map_or(Value::Null, |v| Value::String(v.to_string())),
68
69 "DATETIME" => self
70 .try_get::<NaiveDateTime, _>(idx)
71 .map_or(Value::Null, |v| Value::String(format!("{}T{}", v.date(), v.time()))),
72
73 "TIMESTAMP" => self.try_get::<DateTime<Utc>, _>(idx).map_or(Value::Null, |v| {
77 let n = v.naive_utc();
78 Value::String(format!("{}T{}", n.date(), n.time()))
79 }),
80
81 _ => self
83 .try_get::<String, _>(idx)
84 .map_or_else(|_| bytes_to_json(self, idx), Value::String),
85 }
86 };
87
88 map.insert(column.name().to_string(), value);
89 }
90
91 Value::Object(map)
92 }
93}
94
95fn bytes_to_json(row: &MySqlRow, idx: usize) -> Value {
97 row.try_get::<Vec<u8>, _>(idx).map_or(Value::Null, |bytes| {
98 String::from_utf8(bytes.clone()).map_or_else(|_| Value::String(BASE64.encode(&bytes)), Value::String)
99 })
100}