11mod sql;
22mod sql_pseudofunctions;
3+ mod sql_to_json;
34
45use anyhow:: { anyhow, Context } ;
56use futures_util:: stream:: Stream ;
67use futures_util:: StreamExt ;
7- use serde_json:: { Map , Value } ;
8+ use serde_json:: Value ;
89use std:: borrow:: Cow ;
910use std:: fmt:: { Display , Formatter } ;
1011use std:: path:: Path ;
1112use std:: time:: Duration ;
1213
1314use crate :: app_config:: AppConfig ;
1415pub use crate :: file_cache:: FileCache ;
15- use crate :: utils :: add_value_to_map ;
16+
1617use crate :: webserver:: database:: sql_pseudofunctions:: extract_req_param;
1718use crate :: webserver:: http:: RequestInfo ;
1819use crate :: MIGRATIONS_DIR ;
@@ -24,10 +25,7 @@ use sqlx::any::{
2425use sqlx:: migrate:: Migrator ;
2526use sqlx:: pool:: { PoolConnection , PoolOptions } ;
2627use sqlx:: query:: Query ;
27- use sqlx:: {
28- Any , AnyConnection , AnyPool , Arguments , Column , ConnectOptions , Decode , Either , Executor , Row ,
29- Statement , TypeInfo , ValueRef ,
30- } ;
28+ use sqlx:: { Any , AnyConnection , AnyPool , Arguments , ConnectOptions , Either , Executor , Statement } ;
3129
3230use self :: sql:: ParsedSQLStatement ;
3331
@@ -160,7 +158,7 @@ async fn take_connection<'a, 'b>(
160158#[ inline]
161159fn parse_single_sql_result ( res : sqlx:: Result < Either < AnyQueryResult , AnyRow > > ) -> DbItem {
162160 match res {
163- Ok ( Either :: Right ( r) ) => DbItem :: Row ( row_to_json ( & r) ) ,
161+ Ok ( Either :: Right ( r) ) => DbItem :: Row ( sql_to_json :: row_to_json ( & r) ) ,
164162 Ok ( Either :: Left ( res) ) => {
165163 log:: debug!( "Finished query with result: {:?}" , res) ;
166164 DbItem :: FinishedQuery
@@ -202,105 +200,6 @@ pub enum DbItem {
202200 Error ( anyhow:: Error ) ,
203201}
204202
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-
217- fn row_to_json ( row : & AnyRow ) -> Value {
218- use Value :: Object ;
219-
220- let columns = row. columns ( ) ;
221- let mut map = Map :: new ( ) ;
222- for col in columns {
223- let key = col. name ( ) . to_string ( ) ;
224- let value: Value = sql_to_json ( row, col) ;
225- map = add_value_to_map ( map, ( key, value) ) ;
226- }
227- Object ( map)
228- }
229-
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
238- . take ( )
239- . unwrap_or_else ( || row. try_get_raw ( col. ordinal ( ) ) . unwrap ( ) )
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_or_else ( std:: string:: ToString :: to_string, ToString :: to_string)
275- . into ( ) ,
276- "TIME" => <chrono:: NaiveTime as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
277- . as_ref ( )
278- . map_or_else ( ToString :: to_string, ToString :: to_string)
279- . into ( ) ,
280- "DATETIME" | "DATETIME2" | "DATETIMEOFFSET" | "TIMESTAMP" | "TIMESTAMPTZ" => {
281- try_decode_with ! (
282- get_ref( ) ,
283- [ chrono:: NaiveDateTime , chrono:: DateTime <chrono:: Utc >] ,
284- |v| dbg!( v) . to_string( )
285- )
286- . unwrap_or_else ( |e| format ! ( "Unable to decode date: {e:?}" ) )
287- . into ( )
288- }
289- "JSON" | "JSON[]" | "JSONB" | "JSONB[]" => {
290- <& [ u8 ] as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
291- . and_then ( |rv| {
292- serde_json:: from_slice :: < Value > ( rv)
293- . map_err ( |e| Box :: new ( e) as Box < dyn std:: error:: Error + Sync + Send > )
294- } )
295- . unwrap_or_default ( )
296- }
297- // Deserialize as a string by default
298- _ => <String as Decode < sqlx:: any:: Any > >:: decode ( raw_value)
299- . unwrap_or_default ( )
300- . into ( ) ,
301- }
302- }
303-
304203impl Database {
305204 pub async fn init ( config : & AppConfig ) -> anyhow:: Result < Self > {
306205 let database_url = & config. database_url ;
@@ -380,30 +279,3 @@ impl Display for PreparedStatement {
380279 write ! ( f, "{}" , self . statement. sql( ) )
381280 }
382281}
383-
384- #[ actix_web:: test]
385- async fn test_row_to_json ( ) -> anyhow:: Result < ( ) > {
386- use sqlx:: Connection ;
387- let mut c = sqlx:: AnyConnection :: connect ( "sqlite://:memory:" ) . await ?;
388- let row = sqlx:: query (
389- "SELECT \
390- 123.456 as one_value, \
391- 1 as two_values, \
392- 2 as two_values, \
393- 'x' as three_values, \
394- 'y' as three_values, \
395- 'z' as three_values \
396- ",
397- )
398- . fetch_one ( & mut c)
399- . await ?;
400- assert_eq ! (
401- row_to_json( & row) ,
402- serde_json:: json!( {
403- "one_value" : 123.456 ,
404- "two_values" : [ 1 , 2 ] ,
405- "three_values" : [ "x" , "y" , "z" ] ,
406- } )
407- ) ;
408- Ok ( ( ) )
409- }
0 commit comments