@@ -2,8 +2,9 @@ use anyhow::{anyhow, Result};
2
2
use native_tls:: TlsConnector ;
3
3
use postgres_native_tls:: MakeTlsConnector ;
4
4
use spin_world:: async_trait;
5
- use spin_world:: v2:: postgres:: { self as v2} ;
6
- use spin_world:: v2:: rdbms_types:: { Column , DbDataType , DbValue , ParameterValue , RowSet } ;
5
+ use spin_world:: spin:: postgres:: postgres:: {
6
+ self as v3, Column , DbDataType , DbValue , ParameterValue , RowSet ,
7
+ } ;
7
8
use tokio_postgres:: types:: Type ;
8
9
use tokio_postgres:: { config:: SslMode , types:: ToSql , Row } ;
9
10
use tokio_postgres:: { Client as TokioClient , NoTls , Socket } ;
@@ -18,13 +19,13 @@ pub trait Client {
18
19
& self ,
19
20
statement : String ,
20
21
params : Vec < ParameterValue > ,
21
- ) -> Result < u64 , v2 :: Error > ;
22
+ ) -> Result < u64 , v3 :: Error > ;
22
23
23
24
async fn query (
24
25
& self ,
25
26
statement : String ,
26
27
params : Vec < ParameterValue > ,
27
- ) -> Result < RowSet , v2 :: Error > ;
28
+ ) -> Result < RowSet , v3 :: Error > ;
28
29
}
29
30
30
31
#[ async_trait]
@@ -54,33 +55,43 @@ impl Client for TokioClient {
54
55
& self ,
55
56
statement : String ,
56
57
params : Vec < ParameterValue > ,
57
- ) -> Result < u64 , v2 :: Error > {
58
- let params: Vec < & ( dyn ToSql + Sync ) > = params
58
+ ) -> Result < u64 , v3 :: Error > {
59
+ let params = params
59
60
. iter ( )
60
61
. map ( to_sql_parameter)
61
62
. collect :: < Result < Vec < _ > > > ( )
62
- . map_err ( |e| v2 :: Error :: ValueConversionFailed ( format ! ( "{:?}" , e) ) ) ?;
63
+ . map_err ( |e| v3 :: Error :: ValueConversionFailed ( format ! ( "{:?}" , e) ) ) ?;
63
64
64
- self . execute ( & statement, params. as_slice ( ) )
65
+ let params_refs: Vec < & ( dyn ToSql + Sync ) > = params
66
+ . iter ( )
67
+ . map ( |b| b. as_ref ( ) as & ( dyn ToSql + Sync ) )
68
+ . collect ( ) ;
69
+
70
+ self . execute ( & statement, params_refs. as_slice ( ) )
65
71
. await
66
- . map_err ( |e| v2 :: Error :: QueryFailed ( format ! ( "{:?}" , e) ) )
72
+ . map_err ( |e| v3 :: Error :: QueryFailed ( format ! ( "{:?}" , e) ) )
67
73
}
68
74
69
75
async fn query (
70
76
& self ,
71
77
statement : String ,
72
78
params : Vec < ParameterValue > ,
73
- ) -> Result < RowSet , v2 :: Error > {
74
- let params: Vec < & ( dyn ToSql + Sync ) > = params
79
+ ) -> Result < RowSet , v3 :: Error > {
80
+ let params = params
75
81
. iter ( )
76
82
. map ( to_sql_parameter)
77
83
. collect :: < Result < Vec < _ > > > ( )
78
- . map_err ( |e| v2:: Error :: BadParameter ( format ! ( "{:?}" , e) ) ) ?;
84
+ . map_err ( |e| v3:: Error :: BadParameter ( format ! ( "{:?}" , e) ) ) ?;
85
+
86
+ let params_refs: Vec < & ( dyn ToSql + Sync ) > = params
87
+ . iter ( )
88
+ . map ( |b| b. as_ref ( ) as & ( dyn ToSql + Sync ) )
89
+ . collect ( ) ;
79
90
80
91
let results = self
81
- . query ( & statement, params . as_slice ( ) )
92
+ . query ( & statement, params_refs . as_slice ( ) )
82
93
. await
83
- . map_err ( |e| v2 :: Error :: QueryFailed ( format ! ( "{:?}" , e) ) ) ?;
94
+ . map_err ( |e| v3 :: Error :: QueryFailed ( format ! ( "{:?}" , e) ) ) ?;
84
95
85
96
if results. is_empty ( ) {
86
97
return Ok ( RowSet {
@@ -94,7 +105,7 @@ impl Client for TokioClient {
94
105
. iter ( )
95
106
. map ( convert_row)
96
107
. collect :: < Result < Vec < _ > , _ > > ( )
97
- . map_err ( |e| v2 :: Error :: QueryFailed ( format ! ( "{:?}" , e) ) ) ?;
108
+ . map_err ( |e| v3 :: Error :: QueryFailed ( format ! ( "{:?}" , e) ) ) ?;
98
109
99
110
Ok ( RowSet { columns, rows } )
100
111
}
@@ -111,22 +122,43 @@ where
111
122
} ) ;
112
123
}
113
124
114
- fn to_sql_parameter ( value : & ParameterValue ) -> Result < & ( dyn ToSql + Sync ) > {
125
+ fn to_sql_parameter ( value : & ParameterValue ) -> Result < Box < dyn ToSql + Send + Sync > > {
115
126
match value {
116
- ParameterValue :: Boolean ( v) => Ok ( v) ,
117
- ParameterValue :: Int32 ( v) => Ok ( v) ,
118
- ParameterValue :: Int64 ( v) => Ok ( v) ,
119
- ParameterValue :: Int8 ( v) => Ok ( v) ,
120
- ParameterValue :: Int16 ( v) => Ok ( v) ,
121
- ParameterValue :: Floating32 ( v) => Ok ( v) ,
122
- ParameterValue :: Floating64 ( v) => Ok ( v) ,
123
- ParameterValue :: Uint8 ( _)
124
- | ParameterValue :: Uint16 ( _)
125
- | ParameterValue :: Uint32 ( _)
126
- | ParameterValue :: Uint64 ( _) => Err ( anyhow ! ( "Postgres does not support unsigned integers" ) ) ,
127
- ParameterValue :: Str ( v) => Ok ( v) ,
128
- ParameterValue :: Binary ( v) => Ok ( v) ,
129
- ParameterValue :: DbNull => Ok ( & PgNull ) ,
127
+ ParameterValue :: Boolean ( v) => Ok ( Box :: new ( * v) ) ,
128
+ ParameterValue :: Int32 ( v) => Ok ( Box :: new ( * v) ) ,
129
+ ParameterValue :: Int64 ( v) => Ok ( Box :: new ( * v) ) ,
130
+ ParameterValue :: Int8 ( v) => Ok ( Box :: new ( * v) ) ,
131
+ ParameterValue :: Int16 ( v) => Ok ( Box :: new ( * v) ) ,
132
+ ParameterValue :: Floating32 ( v) => Ok ( Box :: new ( * v) ) ,
133
+ ParameterValue :: Floating64 ( v) => Ok ( Box :: new ( * v) ) ,
134
+ ParameterValue :: Str ( v) => Ok ( Box :: new ( v. clone ( ) ) ) ,
135
+ ParameterValue :: Binary ( v) => Ok ( Box :: new ( v. clone ( ) ) ) ,
136
+ ParameterValue :: Date ( ( y, mon, d) ) => {
137
+ let naive_date = chrono:: NaiveDate :: from_ymd_opt ( * y, ( * mon) . into ( ) , ( * d) . into ( ) )
138
+ . ok_or_else ( || anyhow ! ( "invalid date y={y}, m={mon}, d={d}" ) ) ?;
139
+ Ok ( Box :: new ( naive_date) )
140
+ }
141
+ ParameterValue :: Time ( ( h, min, s, ns) ) => {
142
+ let naive_time =
143
+ chrono:: NaiveTime :: from_hms_nano_opt ( ( * h) . into ( ) , ( * min) . into ( ) , ( * s) . into ( ) , * ns)
144
+ . ok_or_else ( || anyhow ! ( "invalid time {h}:{min}:{s}:{ns}" ) ) ?;
145
+ Ok ( Box :: new ( naive_time) )
146
+ }
147
+ ParameterValue :: Datetime ( ( y, mon, d, h, min, s, ns) ) => {
148
+ let naive_date = chrono:: NaiveDate :: from_ymd_opt ( * y, ( * mon) . into ( ) , ( * d) . into ( ) )
149
+ . ok_or_else ( || anyhow ! ( "invalid date y={y}, m={mon}, d={d}" ) ) ?;
150
+ let naive_time =
151
+ chrono:: NaiveTime :: from_hms_nano_opt ( ( * h) . into ( ) , ( * min) . into ( ) , ( * s) . into ( ) , * ns)
152
+ . ok_or_else ( || anyhow ! ( "invalid time {h}:{min}:{s}:{ns}" ) ) ?;
153
+ let dt = chrono:: NaiveDateTime :: new ( naive_date, naive_time) ;
154
+ Ok ( Box :: new ( dt) )
155
+ }
156
+ ParameterValue :: Timestamp ( v) => {
157
+ let ts = chrono:: DateTime :: < chrono:: Utc > :: from_timestamp ( * v, 0 )
158
+ . ok_or_else ( || anyhow ! ( "invalid epoch timestamp {v}" ) ) ?;
159
+ Ok ( Box :: new ( ts) )
160
+ }
161
+ ParameterValue :: DbNull => Ok ( Box :: new ( PgNull ) ) ,
130
162
}
131
163
}
132
164
@@ -155,22 +187,25 @@ fn convert_data_type(pg_type: &Type) -> DbDataType {
155
187
Type :: INT4 => DbDataType :: Int32 ,
156
188
Type :: INT8 => DbDataType :: Int64 ,
157
189
Type :: TEXT | Type :: VARCHAR | Type :: BPCHAR => DbDataType :: Str ,
190
+ Type :: TIMESTAMP | Type :: TIMESTAMPTZ => DbDataType :: Timestamp ,
191
+ Type :: DATE => DbDataType :: Date ,
192
+ Type :: TIME => DbDataType :: Time ,
158
193
_ => {
159
194
tracing:: debug!( "Couldn't convert Postgres type {} to WIT" , pg_type. name( ) , ) ;
160
195
DbDataType :: Other
161
196
}
162
197
}
163
198
}
164
199
165
- fn convert_row ( row : & Row ) -> Result < Vec < DbValue > , tokio_postgres :: Error > {
200
+ fn convert_row ( row : & Row ) -> anyhow :: Result < Vec < DbValue > > {
166
201
let mut result = Vec :: with_capacity ( row. len ( ) ) ;
167
202
for index in 0 ..row. len ( ) {
168
203
result. push ( convert_entry ( row, index) ?) ;
169
204
}
170
205
Ok ( result)
171
206
}
172
207
173
- fn convert_entry ( row : & Row , index : usize ) -> Result < DbValue , tokio_postgres :: Error > {
208
+ fn convert_entry ( row : & Row , index : usize ) -> anyhow :: Result < DbValue > {
174
209
let column = & row. columns ( ) [ index] ;
175
210
let value = match column. type_ ( ) {
176
211
& Type :: BOOL => {
@@ -229,6 +264,27 @@ fn convert_entry(row: &Row, index: usize) -> Result<DbValue, tokio_postgres::Err
229
264
None => DbValue :: DbNull ,
230
265
}
231
266
}
267
+ & Type :: TIMESTAMP | & Type :: TIMESTAMPTZ => {
268
+ let value: Option < chrono:: NaiveDateTime > = row. try_get ( index) ?;
269
+ match value {
270
+ Some ( v) => DbValue :: Datetime ( tuplify_date_time ( v) ?) ,
271
+ None => DbValue :: DbNull ,
272
+ }
273
+ }
274
+ & Type :: DATE => {
275
+ let value: Option < chrono:: NaiveDate > = row. try_get ( index) ?;
276
+ match value {
277
+ Some ( v) => DbValue :: Date ( tuplify_date ( v) ?) ,
278
+ None => DbValue :: DbNull ,
279
+ }
280
+ }
281
+ & Type :: TIME => {
282
+ let value: Option < chrono:: NaiveTime > = row. try_get ( index) ?;
283
+ match value {
284
+ Some ( v) => DbValue :: Time ( tuplify_time ( v) ?) ,
285
+ None => DbValue :: DbNull ,
286
+ }
287
+ }
232
288
t => {
233
289
tracing:: debug!(
234
290
"Couldn't convert Postgres type {} in column {}" ,
@@ -241,6 +297,41 @@ fn convert_entry(row: &Row, index: usize) -> Result<DbValue, tokio_postgres::Err
241
297
Ok ( value)
242
298
}
243
299
300
+ // Functions to convert from the chrono types to the WIT interface tuples
301
+ fn tuplify_date_time (
302
+ value : chrono:: NaiveDateTime ,
303
+ ) -> anyhow:: Result < ( i32 , u8 , u8 , u8 , u8 , u8 , u32 ) > {
304
+ use chrono:: { Datelike , Timelike } ;
305
+ Ok ( (
306
+ value. year ( ) ,
307
+ value. month ( ) . try_into ( ) ?,
308
+ value. day ( ) . try_into ( ) ?,
309
+ value. hour ( ) . try_into ( ) ?,
310
+ value. minute ( ) . try_into ( ) ?,
311
+ value. second ( ) . try_into ( ) ?,
312
+ value. nanosecond ( ) ,
313
+ ) )
314
+ }
315
+
316
+ fn tuplify_date ( value : chrono:: NaiveDate ) -> anyhow:: Result < ( i32 , u8 , u8 ) > {
317
+ use chrono:: Datelike ;
318
+ Ok ( (
319
+ value. year ( ) ,
320
+ value. month ( ) . try_into ( ) ?,
321
+ value. day ( ) . try_into ( ) ?,
322
+ ) )
323
+ }
324
+
325
+ fn tuplify_time ( value : chrono:: NaiveTime ) -> anyhow:: Result < ( u8 , u8 , u8 , u32 ) > {
326
+ use chrono:: Timelike ;
327
+ Ok ( (
328
+ value. hour ( ) . try_into ( ) ?,
329
+ value. minute ( ) . try_into ( ) ?,
330
+ value. second ( ) . try_into ( ) ?,
331
+ value. nanosecond ( ) ,
332
+ ) )
333
+ }
334
+
244
335
/// Although the Postgres crate converts Rust Option::None to Postgres NULL,
245
336
/// it enforces the type of the Option as it does so. (For example, trying to
246
337
/// pass an Option::<i32>::None to a VARCHAR column fails conversion.) As we
0 commit comments