@@ -139,33 +139,34 @@ fn anyvalue_to_sqlite(value: AnyValue, _dtype: &DataType) -> rusqlite::types::Va
139139 AnyValue :: Float64 ( v) => Value :: Real ( v) ,
140140 AnyValue :: String ( s) => Value :: Text ( s. to_string ( ) ) ,
141141 AnyValue :: StringOwned ( s) => Value :: Text ( s. to_string ( ) ) ,
142- AnyValue :: Date ( days) => {
143- // Convert days since epoch to ISO-8601 date string
144- match chrono:: NaiveDate :: from_num_days_from_ce_opt ( days + 719_163 ) {
145- Some ( d) => Value :: Text ( d. format ( "%Y-%m-%d" ) . to_string ( ) ) ,
146- None => Value :: Null ,
147- }
148- }
149- AnyValue :: Datetime ( us, _, _) => {
150- // Convert microseconds since epoch to ISO-8601 datetime string
151- match chrono:: DateTime :: from_timestamp_micros ( us) {
152- Some ( d) => Value :: Text ( d. format ( "%Y-%m-%dT%H:%M:%S%.f" ) . to_string ( ) ) ,
153- None => Value :: Null ,
154- }
155- }
142+ AnyValue :: Date ( days) => chrono:: NaiveDate :: from_num_days_from_ce_opt ( days + 719_163 )
143+ . and_then ( |d| to_sql_value ( & d) )
144+ . unwrap_or ( Value :: Null ) ,
145+ AnyValue :: Datetime ( us, _, _) => chrono:: DateTime :: from_timestamp_micros ( us)
146+ . map ( |d| d. naive_utc ( ) )
147+ . and_then ( |d| to_sql_value ( & d) )
148+ . unwrap_or ( Value :: Null ) ,
156149 AnyValue :: Time ( ns) => {
157- // Convert nanoseconds since midnight to time string
158150 let secs = ( ns / 1_000_000_000 ) as u32 ;
159151 let nanos = ( ns % 1_000_000_000 ) as u32 ;
160- match chrono:: NaiveTime :: from_num_seconds_from_midnight_opt ( secs, nanos) {
161- Some ( t) => Value :: Text ( t. format ( "%H:%M:%S%.f" ) . to_string ( ) ) ,
162- None => Value :: Null ,
163- }
152+ chrono:: NaiveTime :: from_num_seconds_from_midnight_opt ( secs, nanos)
153+ . and_then ( |t| to_sql_value ( & t) )
154+ . unwrap_or ( Value :: Null )
164155 }
165156 _ => Value :: Text ( format ! ( "{}" , value) ) ,
166157 }
167158}
168159
160+ /// Use rusqlite's `ToSql` to convert a value into a `rusqlite::types::Value`.
161+ fn to_sql_value ( v : & dyn rusqlite:: types:: ToSql ) -> Option < rusqlite:: types:: Value > {
162+ use rusqlite:: types:: ToSqlOutput ;
163+ match v. to_sql ( ) . ok ( ) ? {
164+ ToSqlOutput :: Borrowed ( vref) => Some ( vref. into ( ) ) ,
165+ ToSqlOutput :: Owned ( val) => Some ( val) ,
166+ _ => None ,
167+ }
168+ }
169+
169170impl Reader for SqliteReader {
170171 fn execute_sql ( & self , sql : & str ) -> Result < DataFrame > {
171172 // Handle ggsql:name namespaced identifiers (builtin datasets)
@@ -399,7 +400,7 @@ impl Reader for SqliteReader {
399400/// Try to parse all non-null TEXT values as ISO-8601 dates (YYYY-MM-DD).
400401/// Returns a Date series if all non-null values parse, None otherwise.
401402fn try_parse_as_date ( name : & str , values : & [ rusqlite:: types:: Value ] ) -> Option < Series > {
402- use rusqlite:: types:: Value ;
403+ use rusqlite:: types:: { FromSql , Value , ValueRef } ;
403404
404405 // Days between 0001-01-01 (CE day 1) and 1970-01-01 (Unix epoch)
405406 const EPOCH_DAYS_FROM_CE : i32 = 719_163 ;
@@ -410,7 +411,8 @@ fn try_parse_as_date(name: &str, values: &[rusqlite::types::Value]) -> Option<Se
410411 match v {
411412 Value :: Null => parsed. push ( None ) ,
412413 Value :: Text ( s) => {
413- let date = chrono:: NaiveDate :: parse_from_str ( s, "%Y-%m-%d" ) . ok ( ) ?;
414+ let vref = ValueRef :: Text ( s. as_bytes ( ) ) ;
415+ let date: chrono:: NaiveDate = FromSql :: column_result ( vref) . ok ( ) ?;
414416 parsed. push ( Some ( date. num_days_from_ce ( ) - EPOCH_DAYS_FROM_CE ) ) ;
415417 }
416418 _ => return None ,
@@ -425,7 +427,7 @@ fn try_parse_as_date(name: &str, values: &[rusqlite::types::Value]) -> Option<Se
425427/// Supports both "T" and space separators (e.g. "2024-01-15T10:30:00" or "2024-01-15 10:30:00").
426428/// Returns a Datetime series if all non-null values parse, None otherwise.
427429fn try_parse_as_datetime ( name : & str , values : & [ rusqlite:: types:: Value ] ) -> Option < Series > {
428- use rusqlite:: types:: Value ;
430+ use rusqlite:: types:: { FromSql , Value , ValueRef } ;
429431
430432 let mut parsed: Vec < Option < i64 > > = Vec :: with_capacity ( values. len ( ) ) ;
431433
@@ -437,10 +439,8 @@ fn try_parse_as_datetime(name: &str, values: &[rusqlite::types::Value]) -> Optio
437439 if !s. contains ( 'T' ) && !s. contains ( ' ' ) {
438440 return None ;
439441 }
440- let dt = s
441- . parse :: < chrono:: NaiveDateTime > ( )
442- . or_else ( |_| chrono:: NaiveDateTime :: parse_from_str ( s, "%Y-%m-%dT%H:%M:%S%.f" ) )
443- . ok ( ) ?;
442+ let vref = ValueRef :: Text ( s. as_bytes ( ) ) ;
443+ let dt: chrono:: NaiveDateTime = FromSql :: column_result ( vref) . ok ( ) ?;
444444 parsed. push ( Some ( dt. and_utc ( ) . timestamp_millis ( ) ) ) ;
445445 }
446446 _ => return None ,
0 commit comments