Skip to main content

sea_orm/query/
json.rs

1use crate::{FromQueryResult, QueryResult, error::*};
2use serde_json::Map;
3pub use serde_json::Value as JsonValue;
4
5impl FromQueryResult for JsonValue {
6    /// # Warnings
7    /// If the database does not provide a data type for a value, that value may be lost.
8    #[allow(unused_variables, unused_mut)]
9    fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr> {
10        let mut map = Map::new();
11        #[allow(unused_macros)]
12        macro_rules! try_get_type {
13            ( $type: ty, $col: ident ) => {
14                if let Ok(v) = res.try_get::<Option<$type>>(pre, &$col) {
15                    map.insert($col.to_owned(), json!(v));
16                    continue;
17                }
18            };
19        }
20        match &res.row {
21            #[cfg(feature = "sqlx-mysql")]
22            crate::QueryResultRow::SqlxMySql(row) => {
23                use serde_json::json;
24                use sqlx::{Column, MySql, Row, Type, TypeInfo};
25                for column in row.columns() {
26                    let col = if !column.name().starts_with(pre) {
27                        continue;
28                    } else {
29                        column.name().replacen(pre, "", 1)
30                    };
31                    let col_type = column.type_info();
32                    macro_rules! match_mysql_type {
33                        ( $type: ty ) => {
34                            if <$type as Type<MySql>>::type_info().eq(col_type) {
35                                try_get_type!($type, col)
36                            }
37                        };
38                    }
39                    macro_rules! match_mysql_compatible_type {
40                        ( $type: ty ) => {
41                            if <$type as Type<MySql>>::compatible(col_type) {
42                                try_get_type!($type, col)
43                            }
44                        };
45                    }
46                    match_mysql_type!(bool);
47                    match_mysql_type!(i8);
48                    match_mysql_type!(i16);
49                    match_mysql_type!(i32);
50                    match_mysql_type!(i64);
51                    match_mysql_type!(u8);
52                    match_mysql_type!(u16);
53                    match_mysql_type!(u32);
54                    match_mysql_type!(u64);
55                    match_mysql_type!(f32);
56                    match_mysql_type!(f64);
57                    #[cfg(feature = "with-json")]
58                    // MariaDB reports JSON columns as text-compatible BLOBs.
59                    if col_type.name().eq_ignore_ascii_case("JSON")
60                        || (col_type.name().eq_ignore_ascii_case("BLOB")
61                            && <String as Type<MySql>>::compatible(col_type))
62                    {
63                        try_get_type!(serde_json::Value, col);
64                    }
65                    match_mysql_type!(String);
66                    #[cfg(feature = "with-chrono")]
67                    match_mysql_type!(chrono::NaiveDate);
68                    #[cfg(feature = "with-chrono")]
69                    match_mysql_type!(chrono::NaiveTime);
70                    #[cfg(feature = "with-chrono")]
71                    match_mysql_type!(chrono::NaiveDateTime);
72                    #[cfg(feature = "with-chrono")]
73                    match_mysql_type!(chrono::DateTime<chrono::Utc>);
74                    #[cfg(feature = "with-time")]
75                    match_mysql_type!(time::Date);
76                    #[cfg(feature = "with-time")]
77                    match_mysql_type!(time::Time);
78                    #[cfg(feature = "with-time")]
79                    match_mysql_type!(time::PrimitiveDateTime);
80                    #[cfg(feature = "with-time")]
81                    match_mysql_type!(time::OffsetDateTime);
82                    #[cfg(feature = "with-rust_decimal")]
83                    match_mysql_type!(rust_decimal::Decimal);
84                    match_mysql_compatible_type!(String);
85                    #[cfg(feature = "with-uuid")]
86                    try_get_type!(uuid::Uuid, col);
87                    try_get_type!(Vec<u8>, col);
88                }
89                Ok(JsonValue::Object(map))
90            }
91            #[cfg(feature = "sqlx-postgres")]
92            crate::QueryResultRow::SqlxPostgres(row) => {
93                use serde_json::json;
94                use sqlx::{Column, Postgres, Row, Type, postgres::types::Oid};
95
96                for column in row.columns() {
97                    let col = if !column.name().starts_with(pre) {
98                        continue;
99                    } else {
100                        column.name().replacen(pre, "", 1)
101                    };
102                    let col_type = column.type_info();
103
104                    macro_rules! match_postgres_type {
105                        ( $type: ty ) => {
106                            match col_type.kind() {
107                                #[cfg(feature = "postgres-array")]
108                                sqlx::postgres::PgTypeKind::Array(_) => {
109                                    if <Vec<$type> as Type<Postgres>>::type_info().eq(col_type) {
110                                        try_get_type!(Vec<$type>, col);
111                                    }
112                                }
113                                _ => {
114                                    if <$type as Type<Postgres>>::type_info().eq(col_type) {
115                                        try_get_type!($type, col);
116                                    }
117                                }
118                            }
119                        };
120                    }
121
122                    match_postgres_type!(bool);
123                    match_postgres_type!(i8);
124                    match_postgres_type!(i16);
125                    match_postgres_type!(i32);
126                    match_postgres_type!(i64);
127                    // match_postgres_type!(u8); // unsupported by SQLx Postgres
128                    // match_postgres_type!(u16); // unsupported by SQLx Postgres
129                    // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`;
130                    // Instead, `u32` was wrapped by a `sqlx::Oid`.
131                    if <Oid as Type<Postgres>>::type_info().eq(col_type) {
132                        try_get_type!(u32, col)
133                    }
134                    // match_postgres_type!(u64); // unsupported by SQLx Postgres
135                    match_postgres_type!(f32);
136                    match_postgres_type!(f64);
137                    #[cfg(feature = "with-chrono")]
138                    match_postgres_type!(chrono::NaiveDate);
139                    #[cfg(feature = "with-chrono")]
140                    match_postgres_type!(chrono::NaiveTime);
141                    #[cfg(feature = "with-chrono")]
142                    match_postgres_type!(chrono::NaiveDateTime);
143                    #[cfg(feature = "with-chrono")]
144                    match_postgres_type!(chrono::DateTime<chrono::FixedOffset>);
145                    #[cfg(feature = "with-time")]
146                    match_postgres_type!(time::Date);
147                    #[cfg(feature = "with-time")]
148                    match_postgres_type!(time::Time);
149                    #[cfg(feature = "with-time")]
150                    match_postgres_type!(time::PrimitiveDateTime);
151                    #[cfg(feature = "with-time")]
152                    match_postgres_type!(time::OffsetDateTime);
153                    #[cfg(feature = "with-rust_decimal")]
154                    match_postgres_type!(rust_decimal::Decimal);
155                    #[cfg(feature = "with-json")]
156                    try_get_type!(serde_json::Value, col);
157                    #[cfg(all(feature = "with-json", feature = "postgres-array"))]
158                    try_get_type!(Vec<serde_json::Value>, col);
159                    try_get_type!(String, col);
160                    #[cfg(feature = "postgres-array")]
161                    try_get_type!(Vec<String>, col);
162                    #[cfg(feature = "postgres-vector")]
163                    try_get_type!(pgvector::Vector, col);
164                    #[cfg(feature = "with-uuid")]
165                    try_get_type!(uuid::Uuid, col);
166                    #[cfg(all(feature = "with-uuid", feature = "postgres-array"))]
167                    try_get_type!(Vec<uuid::Uuid>, col);
168                    #[cfg(feature = "with-ipnetwork")]
169                    try_get_type!(ipnetwork::IpNetwork, col);
170                    #[cfg(all(feature = "with-ipnetwork", feature = "postgres-array"))]
171                    try_get_type!(Vec<ipnetwork::IpNetwork>, col);
172                    try_get_type!(Vec<u8>, col);
173                }
174                Ok(JsonValue::Object(map))
175            }
176            #[cfg(feature = "sqlx-sqlite")]
177            crate::QueryResultRow::SqlxSqlite(row) => {
178                use serde_json::json;
179                use sqlx::{Column, Row, Sqlite, Type};
180                for column in row.columns() {
181                    let col = if !column.name().starts_with(pre) {
182                        continue;
183                    } else {
184                        column.name().replacen(pre, "", 1)
185                    };
186                    let col_type = column.type_info();
187                    macro_rules! match_sqlite_type {
188                        ( $type: ty ) => {
189                            if <$type as Type<Sqlite>>::type_info().eq(col_type) {
190                                try_get_type!($type, col)
191                            }
192                        };
193                    }
194                    match_sqlite_type!(bool);
195                    match_sqlite_type!(i8);
196                    match_sqlite_type!(i16);
197                    match_sqlite_type!(i32);
198                    match_sqlite_type!(i64);
199                    match_sqlite_type!(u8);
200                    match_sqlite_type!(u16);
201                    match_sqlite_type!(u32);
202                    // match_sqlite_type!(u64); // unsupported by SQLx Sqlite
203                    match_sqlite_type!(f32);
204                    match_sqlite_type!(f64);
205                    #[cfg(feature = "with-chrono")]
206                    match_sqlite_type!(chrono::NaiveDate);
207                    #[cfg(feature = "with-chrono")]
208                    match_sqlite_type!(chrono::NaiveTime);
209                    #[cfg(feature = "with-chrono")]
210                    match_sqlite_type!(chrono::NaiveDateTime);
211                    #[cfg(feature = "with-time")]
212                    match_sqlite_type!(time::Date);
213                    #[cfg(feature = "with-time")]
214                    match_sqlite_type!(time::Time);
215                    #[cfg(feature = "with-time")]
216                    match_sqlite_type!(time::PrimitiveDateTime);
217                    #[cfg(feature = "with-time")]
218                    match_sqlite_type!(time::OffsetDateTime);
219                    try_get_type!(String, col);
220                    #[cfg(feature = "with-uuid")]
221                    try_get_type!(uuid::Uuid, col);
222                    try_get_type!(Vec<u8>, col);
223                }
224                Ok(JsonValue::Object(map))
225            }
226            #[cfg(feature = "rusqlite")]
227            crate::QueryResultRow::Rusqlite(row) => {
228                use crate::driver::rusqlite::RusqliteOwnedValue;
229                use serde_json::json;
230
231                for (i, column) in row.columns.iter().enumerate() {
232                    let column = if !column.starts_with(pre) {
233                        continue;
234                    } else {
235                        column.replacen(pre, "", 1)
236                    };
237                    map.insert(
238                        column,
239                        match &row.values[i] {
240                            RusqliteOwnedValue::Integer(v) => json!(v),
241                            RusqliteOwnedValue::Real(v) => json!(v),
242                            RusqliteOwnedValue::Text(v) => json!(v),
243                            RusqliteOwnedValue::Blob(v) => json!(v),
244                            RusqliteOwnedValue::Null => json!(null),
245                        },
246                    );
247                }
248                Ok(JsonValue::Object(map))
249            }
250            #[cfg(feature = "mock")]
251            crate::QueryResultRow::Mock(row) => {
252                for (column, value) in row.clone().into_column_value_tuples() {
253                    let col = if !column.starts_with(pre) {
254                        continue;
255                    } else {
256                        column.replacen(pre, "", 1)
257                    };
258                    map.insert(col, sea_query::sea_value_to_json_value(&value));
259                }
260                Ok(JsonValue::Object(map))
261            }
262            #[cfg(feature = "proxy")]
263            crate::QueryResultRow::Proxy(row) => {
264                for (column, value) in row.clone().into_column_value_tuples() {
265                    let col = if !column.starts_with(pre) {
266                        continue;
267                    } else {
268                        column.replacen(pre, "", 1)
269                    };
270                    map.insert(col, sea_query::sea_value_to_json_value(&value));
271                }
272                Ok(JsonValue::Object(map))
273            }
274            #[allow(unreachable_patterns)]
275            _ => unreachable!(),
276        }
277    }
278}
279
280#[cfg(test)]
281#[cfg(feature = "mock")]
282mod tests {
283    use crate::tests_cfg::cake;
284    use crate::{DbBackend, DbErr, MockDatabase, entity::*};
285    use sea_query::Value;
286
287    #[test]
288    fn to_json_1() -> Result<(), DbErr> {
289        let db = MockDatabase::new(DbBackend::Postgres)
290            .append_query_results([[maplit::btreemap! {
291                "id" => Into::<Value>::into(128), "name" => Into::<Value>::into("apple")
292            }]])
293            .into_connection();
294
295        assert_eq!(
296            cake::Entity::find().into_json().one(&db).unwrap(),
297            Some(serde_json::json!({
298                "id": 128,
299                "name": "apple"
300            }))
301        );
302
303        Ok(())
304    }
305}