@@ -26,7 +26,7 @@ use sqlx::pool::{PoolConnection, PoolOptions};
2626use sqlx:: query:: Query ;
2727use sqlx:: {
2828 Any , AnyConnection , AnyPool , Arguments , Column , ConnectOptions , Decode , Either , Executor , Row ,
29- Statement ,
29+ Statement , ValueRef , TypeInfo ,
3030} ;
3131
3232use self :: sql:: ParsedSQLStatement ;
@@ -202,58 +202,108 @@ pub enum DbItem {
202202 Error ( anyhow:: Error ) ,
203203}
204204
205+ macro_rules! try_decode_with {
206+ ( $raw_value: expr, [ $ty0: ty] , $fn: expr) => {
207+ <$ty0 as Decode <sqlx:: any:: Any >>:: decode( $raw_value) . map( $fn)
208+ } ;
209+ ( $raw_value: expr, [ $ty0: ty, $( $ty: ty) ,+] , $fn: expr) => {
210+ match try_decode_with!( $raw_value, [ $ty0] , $fn) {
211+ Ok ( value) => Ok ( value) ,
212+ Err ( _) => try_decode_with!( $raw_value, [ $( $ty) ,+] , $fn) ,
213+ }
214+ } ;
215+ }
216+
205217fn row_to_json ( row : & AnyRow ) -> Value {
206- use sqlx:: { TypeInfo , ValueRef } ;
207- use Value :: { Null , Object } ;
218+ use Value :: Object ;
208219
209220 let columns = row. columns ( ) ;
210221 let mut map = Map :: new ( ) ;
211222 for col in columns {
212223 let key = col. name ( ) . to_string ( ) ;
213- let value: Value = match row. try_get_raw ( col. ordinal ( ) ) {
214- Ok ( raw_value) if !raw_value. is_null ( ) => match raw_value. type_info ( ) . name ( ) {
215- "REAL" | "FLOAT" | "NUMERIC" | "FLOAT4" | "FLOAT8" | "DOUBLE" => {
216- <f64 as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
217- . unwrap_or ( f64:: NAN )
218- . into ( )
219- }
220- "INT8" | "BIGINT" | "INTEGER" => <i64 as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
221- . unwrap_or_default ( )
222- . into ( ) ,
223- "INT" | "INT4" => <i32 as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
224- . unwrap_or_default ( )
225- . into ( ) ,
226- "INT2" | "SMALLINT" => <i16 as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
227- . unwrap_or_default ( )
228- . into ( ) ,
229- "BOOL" | "BOOLEAN" => <bool as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
230- . unwrap_or_default ( )
231- . into ( ) ,
232- "JSON" | "JSON[]" | "JSONB" | "JSONB[]" => {
233- <& [ u8 ] as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
234- . and_then ( |rv| {
235- serde_json:: from_slice :: < Value > ( rv) . map_err ( |e| {
236- Box :: new ( e) as Box < dyn std:: error:: Error + Sync + Send >
237- } )
238- } )
239- . unwrap_or_default ( )
240- }
241- // Deserialize as a string by default
242- _ => <String as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
243- . unwrap_or_default ( )
244- . into ( ) ,
245- } ,
246- Ok ( _null) => Null ,
247- Err ( e) => {
248- log:: warn!( "Unable to extract value from row: {:?}" , e) ;
249- Null
250- }
251- } ;
224+ let value: Value = sql_to_json ( row, col) ;
252225 map = add_value_to_map ( map, ( key, value) ) ;
253226 }
254227 Object ( map)
255228}
256229
230+ fn sql_to_json ( row : & AnyRow , col : & sqlx:: any:: AnyColumn ) -> Value {
231+ let raw_value_result = row. try_get_raw ( col. ordinal ( ) ) ;
232+ match raw_value_result {
233+ Ok ( raw_value) if !raw_value. is_null ( ) => {
234+ let mut raw_value = Some ( raw_value) ;
235+ log:: trace!( "Decoding a value of type {:?}" , col. type_info( ) . name( ) ) ;
236+ let decoded = sql_nonnull_to_json ( || {
237+ raw_value. take ( ) . unwrap_or_else ( || {
238+ row. try_get_raw ( col. ordinal ( ) ) . unwrap ( )
239+ } )
240+ } ) ;
241+ log:: trace!( "Decoded value: {:?}" , decoded) ;
242+ decoded
243+ } ,
244+ Ok ( _null) => Value :: Null ,
245+ Err ( e) => {
246+ log:: warn!( "Unable to extract value from row: {:?}" , e) ;
247+ Value :: Null
248+ }
249+ }
250+ }
251+
252+ fn sql_nonnull_to_json < ' r > ( mut get_ref : impl FnMut ( ) -> sqlx:: any:: AnyValueRef < ' r > ) -> Value {
253+ let raw_value = get_ref ( ) ;
254+ match raw_value. type_info ( ) . name ( ) {
255+ "REAL" | "FLOAT" | "NUMERIC" | "DECIMAL" | "FLOAT4" | "FLOAT8" | "DOUBLE" => {
256+ <f64 as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
257+ . unwrap_or ( f64:: NAN )
258+ . into ( )
259+ }
260+ "INT8" | "BIGINT" | "INTEGER" => <i64 as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
261+ . unwrap_or_default ( )
262+ . into ( ) ,
263+ "INT" | "INT4" => <i32 as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
264+ . unwrap_or_default ( )
265+ . into ( ) ,
266+ "INT2" | "SMALLINT" => <i16 as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
267+ . unwrap_or_default ( )
268+ . into ( ) ,
269+ "BOOL" | "BOOLEAN" => <bool as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
270+ . unwrap_or_default ( )
271+ . into ( ) ,
272+ "DATE" => <chrono:: NaiveDate as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
273+ . as_ref ( )
274+ . map ( ToString :: to_string)
275+ . unwrap_or_else ( |e| e. to_string ( ) )
276+ . into ( ) ,
277+ "TIME" => <chrono:: NaiveTime as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
278+ . as_ref ( )
279+ . map ( ToString :: to_string)
280+ . unwrap_or_else ( |e| e. to_string ( ) )
281+ . into ( ) ,
282+ "DATETIME" | "DATETIME2" | "DATETIMEOFFSET" | "TIMESTAMP" | "TIMESTAMPTZ" => {
283+ try_decode_with ! (
284+ get_ref( ) ,
285+ [ chrono:: NaiveDateTime , chrono:: DateTime <chrono:: Utc >] ,
286+ |v| dbg!( v) . to_string( )
287+ )
288+ . unwrap_or_else ( |e| format ! ( "Unable to decode date: {:?}" , e) )
289+ . into ( )
290+ }
291+ "JSON" | "JSON[]" | "JSONB" | "JSONB[]" => {
292+ <& [ u8 ] as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
293+ . and_then ( |rv| {
294+ serde_json:: from_slice :: < Value > ( rv) . map_err ( |e| {
295+ Box :: new ( e) as Box < dyn std:: error:: Error + Sync + Send >
296+ } )
297+ } )
298+ . unwrap_or_default ( )
299+ }
300+ // Deserialize as a string by default
301+ _ => <String as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
302+ . unwrap_or_default ( )
303+ . into ( ) ,
304+ }
305+ }
306+
257307impl Database {
258308 pub async fn init ( config : & AppConfig ) -> anyhow:: Result < Self > {
259309 let database_url = & config. database_url ;
0 commit comments