11use crate :: utils:: add_value_to_map;
2- use chrono:: { DateTime , Utc } ;
2+ use chrono:: { DateTime , FixedOffset , NaiveDateTime } ;
33use serde_json:: { self , Map , Value } ;
44use sqlx:: any:: { AnyRow , AnyTypeInfo , AnyTypeInfoKind } ;
55use sqlx:: Decode ;
@@ -39,62 +39,54 @@ pub fn sql_to_json(row: &AnyRow, col: &sqlx::any::AnyColumn) -> Value {
3939 }
4040}
4141
42+ fn decode_raw < ' a , T : Decode < ' a , sqlx:: any:: Any > + Default > (
43+ raw_value : sqlx:: any:: AnyValueRef < ' a > ,
44+ ) -> T {
45+ match T :: decode ( raw_value) {
46+ Ok ( v) => v,
47+ Err ( e) => {
48+ let type_name = std:: any:: type_name :: < T > ( ) ;
49+ log:: error!( "Failed to decode {type_name} value: {e}" ) ;
50+ T :: default ( )
51+ }
52+ }
53+ }
54+
4255pub fn sql_nonnull_to_json < ' r > ( mut get_ref : impl FnMut ( ) -> sqlx:: any:: AnyValueRef < ' r > ) -> Value {
4356 let raw_value = get_ref ( ) ;
4457 let type_info = raw_value. type_info ( ) ;
4558 let type_name = type_info. name ( ) ;
4659 log:: trace!( "Decoding a value of type {:?}" , type_name) ;
4760 match type_name {
4861 "REAL" | "FLOAT" | "FLOAT4" | "FLOAT8" | "DOUBLE" | "NUMERIC" | "DECIMAL" => {
49- <f64 as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
50- . unwrap_or ( f64:: NAN )
51- . into ( )
62+ decode_raw :: < f64 > ( raw_value) . into ( )
5263 }
5364 "INT8" | "BIGINT" | "SERIAL8" | "BIGSERIAL" | "IDENTITY" | "INT64" | "INTEGER8"
54- | "BIGINT UNSIGNED" | "BIGINT SIGNED" => <i64 as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
55- . unwrap_or_default ( )
56- . into ( ) ,
57- "INT" | "INT4" | "INTEGER" => <i32 as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
58- . unwrap_or_default ( )
59- . into ( ) ,
60- "INT2" | "SMALLINT" => <i16 as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
61- . unwrap_or_default ( )
62- . into ( ) ,
63- "BOOL" | "BOOLEAN" => <bool as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
64- . unwrap_or_default ( )
65- . into ( ) ,
65+ | "BIGINT UNSIGNED" | "BIGINT SIGNED" => decode_raw :: < i64 > ( raw_value) . into ( ) ,
66+ "INT" | "INT4" | "INTEGER" => decode_raw :: < i32 > ( raw_value) . into ( ) ,
67+ "INT2" | "SMALLINT" => decode_raw :: < i16 > ( raw_value) . into ( ) ,
68+ "BOOL" | "BOOLEAN" => decode_raw :: < bool > ( raw_value) . into ( ) ,
6669 "BIT" if matches ! ( * type_info, AnyTypeInfo ( AnyTypeInfoKind :: Mssql ( _) ) ) => {
67- <bool as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
68- . unwrap_or_default ( )
69- . into ( )
70+ decode_raw :: < bool > ( raw_value) . into ( )
7071 }
71- "DATE" => <chrono:: NaiveDate as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
72- . as_ref ( )
73- . map_or_else ( std:: string:: ToString :: to_string, ToString :: to_string)
72+ "DATE" => decode_raw :: < chrono:: NaiveDate > ( raw_value)
73+ . to_string ( )
7474 . into ( ) ,
75- "TIME" | "TIMETZ" => <chrono:: NaiveTime as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
76- . as_ref ( )
77- . map_or_else ( ToString :: to_string, ToString :: to_string)
75+ "TIME" | "TIMETZ" => decode_raw :: < chrono:: NaiveTime > ( raw_value)
76+ . to_string ( )
7877 . into ( ) ,
79- "DATETIME" | "DATETIME2" | "DATETIMEOFFSET" | "TIMESTAMP" | "TIMESTAMPTZ" => {
80- let mut date_time = <DateTime < Utc > as Decode < sqlx:: any:: Any > >:: decode ( get_ref ( ) ) ;
81- if date_time. is_err ( ) {
82- date_time = <chrono:: NaiveDateTime as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
83- . map ( |d| d. and_utc ( ) ) ;
84- }
85- Value :: String (
86- date_time
87- . as_ref ( )
88- . map_or_else ( ToString :: to_string, DateTime :: to_rfc3339) ,
89- )
90- }
91- "JSON" | "JSON[]" | "JSONB" | "JSONB[]" => {
92- <Value as Decode < sqlx:: any:: Any > >:: decode ( raw_value) . unwrap_or_default ( )
78+ "DATETIMEOFFSET" | "TIMESTAMP" | "TIMESTAMPTZ" => {
79+ decode_raw :: < DateTime < FixedOffset > > ( raw_value)
80+ . to_rfc3339 ( )
81+ . into ( )
9382 }
94- // Deserialize as a string by default
95- _ => < String as Decode < sqlx :: any :: Any > > :: decode ( raw_value )
96- . unwrap_or_default ( )
83+ "DATETIME" | "DATETIME2" => decode_raw :: < NaiveDateTime > ( raw_value )
84+ . format ( "%FT%T%.f" )
85+ . to_string ( )
9786 . into ( ) ,
87+ "JSON" | "JSON[]" | "JSONB" | "JSONB[]" => decode_raw :: < Value > ( raw_value) ,
88+ // Deserialize as a string by default
89+ _ => decode_raw :: < String > ( raw_value) . into ( ) ,
9890 }
9991}
10092
@@ -170,7 +162,8 @@ mod tests {
170162 '2024-03-14'::DATE as date,
171163 '13:14:15'::TIME as time,
172164 '2024-03-14 13:14:15'::TIMESTAMP as timestamp,
173- '2024-03-14 13:14:15+00'::TIMESTAMPTZ as timestamptz,
165+ '2024-03-14 13:14:15+02:00'::TIMESTAMPTZ as timestamptz,
166+ INTERVAL '1 day' as interval,
174167 '{\" key\" : \" value\" }'::JSON as json,
175168 '{\" key\" : \" value\" }'::JSONB as jsonb" ,
176169 )
@@ -189,7 +182,8 @@ mod tests {
189182 "date" : "2024-03-14" ,
190183 "time" : "13:14:15" ,
191184 "timestamp" : "2024-03-14T13:14:15+00:00" ,
192- "timestamptz" : "2024-03-14T13:14:15+00:00" ,
185+ "timestamptz" : "2024-03-14T11:14:15+00:00" , // Postgres stores all timestamps in UTC
186+ "interval" : "1 day" ,
193187 "json" : { "key" : "value" } ,
194188 "jsonb" : { "key" : "value" } ,
195189 } )
@@ -225,7 +219,7 @@ mod tests {
225219 "decimal_number" : 42.25 ,
226220 "date" : "2024-03-14" ,
227221 "time" : "13:14:15" ,
228- "datetime" : "2024-03-14T13:14:15+00:00 " ,
222+ "datetime" : "2024-03-14T13:14:15" ,
229223 "hex_value" : "hello world" ,
230224 "json" : { "key" : "value" } ,
231225 } )
@@ -282,7 +276,7 @@ mod tests {
282276 CAST('13:14:15' AS TIME) as time,
283277 CAST('2024-03-14 13:14:15' AS DATETIME) as datetime,
284278 CAST('2024-03-14 13:14:15' AS DATETIME2) as datetime2,
285- CAST('2024-03-14 13:14:15 +00 :00' AS DATETIMEOFFSET) as datetimeoffset,
279+ CAST('2024-03-14 13:14:15 +02 :00' AS DATETIMEOFFSET) as datetimeoffset,
286280 N'Unicode String' as nvarchar,
287281 'ASCII String' as varchar" ,
288282 )
@@ -303,9 +297,9 @@ mod tests {
303297 "decimal" : 42.25 ,
304298 "date" : "2024-03-14" ,
305299 "time" : "13:14:15" ,
306- "datetime" : "2024-03-14T13:14:15+00:00 " ,
307- "datetime2" : "2024-03-14T13:14:15+00:00 " ,
308- "datetimeoffset" : "2024-03-14T13:14:15+00 :00" ,
300+ "datetime" : "2024-03-14T13:14:15" ,
301+ "datetime2" : "2024-03-14T13:14:15" ,
302+ "datetimeoffset" : "2024-03-14T13:14:15+02 :00" ,
309303 "nvarchar" : "Unicode String" ,
310304 "varchar" : "ASCII String" ,
311305 } )
0 commit comments