@@ -5,7 +5,7 @@ use spin_world::async_trait;
55use spin_world:: spin:: postgres4_0_0:: postgres:: {
66 self as v4, Column , DbDataType , DbValue , ParameterValue , RowSet ,
77} ;
8- use tokio_postgres:: types:: Type ;
8+ use tokio_postgres:: types:: { FromSql , Type } ;
99use tokio_postgres:: { config:: SslMode , types:: ToSql , Row } ;
1010use tokio_postgres:: { Client as TokioClient , NoTls , Socket } ;
1111
@@ -195,6 +195,7 @@ fn to_sql_parameter(value: &ParameterValue) -> Result<Box<dyn ToSql + Send + Syn
195195 ParameterValue :: ArrayInt32 ( vs) => Ok ( Box :: new ( vs. to_owned ( ) ) ) ,
196196 ParameterValue :: ArrayInt64 ( vs) => Ok ( Box :: new ( vs. to_owned ( ) ) ) ,
197197 ParameterValue :: ArrayStr ( vs) => Ok ( Box :: new ( vs. to_owned ( ) ) ) ,
198+ ParameterValue :: Interval ( v) => Ok ( Box :: new ( Interval ( * v) ) ) ,
198199 ParameterValue :: DbNull => Ok ( Box :: new ( PgNull ) ) ,
199200 }
200201}
@@ -403,6 +404,13 @@ fn convert_entry(row: &Row, index: usize) -> anyhow::Result<DbValue> {
403404 None => DbValue :: DbNull ,
404405 }
405406 }
407+ & Type :: INTERVAL => {
408+ let value: Option < Interval > = row. try_get ( index) ?;
409+ match value {
410+ Some ( v) => DbValue :: Interval ( v. 0 ) ,
411+ None => DbValue :: DbNull ,
412+ }
413+ }
406414 t => {
407415 tracing:: debug!(
408416 "Couldn't convert Postgres type {} in column {}" ,
@@ -504,3 +512,80 @@ impl std::fmt::Debug for PgNull {
504512 f. debug_struct ( "NULL" ) . finish ( )
505513 }
506514}
515+
516+ #[ derive( Debug ) ]
517+ struct Interval ( v4:: Interval ) ;
518+
519+ impl ToSql for Interval {
520+ tokio_postgres:: types:: to_sql_checked!( ) ;
521+
522+ fn to_sql (
523+ & self ,
524+ _ty : & Type ,
525+ out : & mut tokio_postgres:: types:: private:: BytesMut ,
526+ ) -> Result < tokio_postgres:: types:: IsNull , Box < dyn std:: error:: Error + Sync + Send > >
527+ where
528+ Self : Sized ,
529+ {
530+ use bytes:: BufMut ;
531+
532+ out. put_i64 ( self . 0 . micros ) ;
533+ out. put_i32 ( self . 0 . days ) ;
534+ out. put_i32 ( self . 0 . months ) ;
535+
536+ Ok ( tokio_postgres:: types:: IsNull :: No )
537+ }
538+
539+ fn accepts ( ty : & Type ) -> bool
540+ where
541+ Self : Sized ,
542+ {
543+ matches ! ( ty, & Type :: INTERVAL )
544+ }
545+ }
546+
547+ impl FromSql < ' _ > for Interval {
548+ fn from_sql (
549+ _ty : & Type ,
550+ raw : & ' _ [ u8 ] ,
551+ ) -> std:: result:: Result < Self , Box < dyn std:: error:: Error + Sync + Send > > {
552+ const EXPECTED_LEN : usize = size_of :: < i64 > ( ) + size_of :: < i32 > ( ) + size_of :: < i32 > ( ) ;
553+
554+ if raw. len ( ) != EXPECTED_LEN {
555+ return Err ( Box :: new ( IntervalLengthError ) ) ;
556+ }
557+
558+ let ( micro_bytes, rest) = raw. split_at ( size_of :: < i64 > ( ) ) ;
559+ let ( day_bytes, rest) = rest. split_at ( size_of :: < i32 > ( ) ) ;
560+ let month_bytes = rest;
561+ let months = i32:: from_be_bytes ( month_bytes. try_into ( ) . unwrap ( ) ) ;
562+ let days = i32:: from_be_bytes ( day_bytes. try_into ( ) . unwrap ( ) ) ;
563+ let micros = i64:: from_be_bytes ( micro_bytes. try_into ( ) . unwrap ( ) ) ;
564+
565+ Ok ( Self ( v4:: Interval {
566+ micros,
567+ days,
568+ months,
569+ } ) )
570+ }
571+
572+ fn accepts ( ty : & Type ) -> bool {
573+ matches ! ( ty, & Type :: INTERVAL )
574+ }
575+ }
576+
577+ struct IntervalLengthError ;
578+
579+ impl std:: error:: Error for IntervalLengthError { }
580+
581+ impl std:: fmt:: Display for IntervalLengthError {
582+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
583+ f. write_str ( "unexpected binary format for Postgres INTERVAL" )
584+ }
585+ }
586+
587+ impl std:: fmt:: Debug for IntervalLengthError {
588+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
589+ std:: fmt:: Display :: fmt ( self , f)
590+ }
591+ }
0 commit comments