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