1+ use std:: sync:: Arc ;
2+
13use anyhow:: { Context , Result } ;
24use futures:: stream:: TryStreamExt as _;
35use native_tls:: TlsConnector ;
46use postgres_native_tls:: MakeTlsConnector ;
57use spin_world:: async_trait;
6- use spin_world:: spin:: postgres4_1_0 :: postgres:: {
8+ use spin_world:: spin:: postgres4_2_0 :: postgres:: {
79 self as v4, Column , DbValue , ParameterValue , RowSet ,
810} ;
9- use std:: pin:: pin;
1011use tokio_postgres:: config:: SslMode ;
1112use tokio_postgres:: types:: ToSql ;
1213use tokio_postgres:: { NoTls , Row } ;
1314
14- use crate :: types:: { convert_data_type, convert_entry, to_sql_parameter} ;
15+ use crate :: types:: { convert_data_type, convert_entry, to_sql_parameter, to_sql_parameters } ;
1516
1617/// Max connections in a given address' connection pool
1718const CONNECTION_POOL_SIZE : usize = 64 ;
@@ -61,9 +62,18 @@ impl Default for PooledTokioClientFactory {
6162 }
6263}
6364
65+ #[ derive( Clone ) ]
66+ pub struct PooledTokioClient ( Arc < deadpool_postgres:: Object > ) ;
67+
68+ impl AsRef < deadpool_postgres:: Object > for PooledTokioClient {
69+ fn as_ref ( & self ) -> & deadpool_postgres:: Object {
70+ self . 0 . as_ref ( )
71+ }
72+ }
73+
6474#[ async_trait]
6575impl ClientFactory for PooledTokioClientFactory {
66- type Client = deadpool_postgres :: Object ;
76+ type Client = PooledTokioClient ;
6777
6878 async fn get_client (
6979 & self ,
@@ -81,7 +91,7 @@ impl ClientFactory for PooledTokioClientFactory {
8191 . map_err ( ArcError )
8292 . context ( "establishing PostgreSQL connection pool" ) ?;
8393
84- Ok ( pool. get ( ) . await ?)
94+ Ok ( PooledTokioClient ( Arc :: new ( pool. get ( ) . await ?) ) )
8595 }
8696}
8797
@@ -123,7 +133,7 @@ fn create_connection_pool(
123133}
124134
125135#[ async_trait]
126- pub trait Client : Send + Sync + ' static {
136+ pub trait Client : Clone + Send + Sync + ' static {
127137 async fn execute (
128138 & self ,
129139 statement : String ,
@@ -136,6 +146,19 @@ pub trait Client: Send + Sync + 'static {
136146 params : Vec < ParameterValue > ,
137147 max_result_bytes : usize ,
138148 ) -> Result < RowSet , v4:: Error > ;
149+
150+ async fn query_async (
151+ & self ,
152+ statement : String ,
153+ params : Vec < ParameterValue > ,
154+ max_result_bytes : usize ,
155+ ) -> Result < QueryAsyncResult , v4:: Error > ;
156+ }
157+
158+ pub struct QueryAsyncResult {
159+ pub columns : Vec < v4:: Column > ,
160+ pub rows : tokio:: sync:: mpsc:: Receiver < v4:: Row > ,
161+ pub error : tokio:: sync:: oneshot:: Receiver < Result < ( ) , v4:: Error > > ,
139162}
140163
141164/// Extract weak-typed error data for WIT purposes
@@ -180,8 +203,13 @@ fn query_failed(e: tokio_postgres::error::Error) -> v4::Error {
180203 v4:: Error :: QueryFailed ( query_error)
181204}
182205
206+ fn query_failed_anyhow ( e : anyhow:: Error ) -> v4:: Error {
207+ let text = format ! ( "{e:?}" ) ;
208+ v4:: Error :: QueryFailed ( v4:: QueryError :: Text ( text) )
209+ }
210+
183211#[ async_trait]
184- impl Client for deadpool_postgres :: Object {
212+ impl Client for PooledTokioClient {
185213 async fn execute (
186214 & self ,
187215 statement : String ,
@@ -210,34 +238,21 @@ impl Client for deadpool_postgres::Object {
210238 params : Vec < ParameterValue > ,
211239 max_result_bytes : usize ,
212240 ) -> Result < RowSet , v4:: Error > {
213- let params = params
214- . iter ( )
215- . map ( to_sql_parameter)
216- . collect :: < Result < Vec < _ > > > ( )
217- . map_err ( |e| v4:: Error :: BadParameter ( format ! ( "{e:?}" ) ) ) ?;
218-
219- let mut results = pin ! ( self
220- . as_ref( )
221- . query_raw( & statement, params)
222- . await
223- . map_err( query_failed) ?) ;
241+ let ( cols_fut, mut results) = self . query_stream ( statement, params) . await ?;
224242
225243 let mut columns = None ;
226244 let mut byte_count = std:: mem:: size_of :: < RowSet > ( ) ;
227245 let mut rows = Vec :: new ( ) ;
228246
229247 async {
230248 while let Some ( row) = results. try_next ( ) . await ? {
231- if columns. is_none ( ) {
232- columns = Some ( infer_columns ( & row) ) ;
233- }
234- let row = convert_row ( & row) ?;
235249 byte_count += row. iter ( ) . map ( |v| v. memory_size ( ) ) . sum :: < usize > ( ) ;
236250 if byte_count > max_result_bytes {
237251 anyhow:: bail!( "query result exceeds limit of {max_result_bytes} bytes" )
238252 }
239253 rows. push ( row) ;
240254 }
255+ columns = Some ( cols_fut. await ) ;
241256 Ok ( ( ) )
242257 }
243258 . await
@@ -248,6 +263,104 @@ impl Client for deadpool_postgres::Object {
248263 rows,
249264 } )
250265 }
266+
267+ async fn query_async (
268+ & self ,
269+ statement : String ,
270+ params : Vec < ParameterValue > ,
271+ max_result_bytes : usize ,
272+ ) -> Result < QueryAsyncResult , v4:: Error > {
273+ use futures:: StreamExt ;
274+
275+ let ( cols_fut, mut rows) = self . query_stream ( statement, params) . await ?;
276+
277+ let ( rows_tx, rows_rx) = tokio:: sync:: mpsc:: channel ( 4 ) ;
278+ let ( err_tx, err_rx) = tokio:: sync:: oneshot:: channel ( ) ;
279+
280+ tokio:: spawn ( async move {
281+ loop {
282+ let Some ( row) = rows. next ( ) . await else {
283+ _ = err_tx. send ( Ok ( ( ) ) ) ;
284+ return ;
285+ } ;
286+ match row {
287+ Ok ( row) => {
288+ let byte_count = row. iter ( ) . map ( |v| v. memory_size ( ) ) . sum :: < usize > ( ) ;
289+ if byte_count > max_result_bytes {
290+ _ = err_tx. send ( Err ( v4:: Error :: QueryFailed ( v4:: QueryError :: Text (
291+ format ! ( "query result exceeds limit of {max_result_bytes} bytes" ) ,
292+ ) ) ) ) ;
293+ return ;
294+ }
295+
296+ if let Err ( e) = rows_tx. send ( row) . await {
297+ _ = err_tx. send ( Err ( v4:: Error :: QueryFailed ( v4:: QueryError :: Text (
298+ format ! ( "async error: {e}" ) ,
299+ ) ) ) ) ;
300+ return ;
301+ }
302+ }
303+ Err ( e) => {
304+ _ = err_tx. send ( Err ( e) ) ;
305+ return ;
306+ }
307+ }
308+ }
309+ } ) ;
310+
311+ let cols = cols_fut. await ;
312+
313+ Ok ( QueryAsyncResult {
314+ columns : cols,
315+ rows : rows_rx,
316+ error : err_rx,
317+ } )
318+ }
319+ }
320+
321+ impl PooledTokioClient {
322+ async fn query_stream (
323+ & self ,
324+ statement : String ,
325+ params : Vec < ParameterValue > ,
326+ ) -> Result <
327+ (
328+ impl std:: future:: Future < Output = Vec < v4:: Column > > ,
329+ impl futures:: Stream < Item = Result < Vec < DbValue > , v4:: Error > > ,
330+ ) ,
331+ v4:: Error ,
332+ > {
333+ use futures:: { FutureExt , StreamExt } ;
334+
335+ let params = to_sql_parameters ( params) ?;
336+
337+ let results = Box :: pin (
338+ self . as_ref ( )
339+ . query_raw ( & statement, params)
340+ . await
341+ . map_err ( query_failed) ?,
342+ ) ;
343+
344+ let ( cols_tx, cols_rx) = tokio:: sync:: oneshot:: channel ( ) ;
345+ let mut cols_tx_opt = Some ( cols_tx) ;
346+
347+ let row_stm = results. enumerate ( ) . map ( move |( index, row_res) | {
348+ let row_res = row_res. map_err ( query_failed) ;
349+ row_res. and_then ( |r| {
350+ if index == 0 {
351+ if let Some ( cols_tx) = cols_tx_opt. take ( ) {
352+ let cols = infer_columns ( & r) ;
353+ _ = cols_tx. send ( cols) ;
354+ }
355+ }
356+ convert_row ( & r) . map_err ( query_failed_anyhow)
357+ } )
358+ } ) ;
359+
360+ let cols_rx = cols_rx. map ( |result| result. unwrap_or_default ( ) ) ;
361+
362+ Ok ( ( cols_rx, Box :: pin ( row_stm) ) )
363+ }
251364}
252365
253366fn infer_columns ( row : & Row ) -> Vec < Column > {
0 commit comments