@@ -7,6 +7,7 @@ use crate::copy_both::CopyBothDuplex;
7
7
use crate :: copy_out:: CopyOutStream ;
8
8
#[ cfg( feature = "runtime" ) ]
9
9
use crate :: keepalive:: KeepaliveConfig ;
10
+ use crate :: prepare:: get_type;
10
11
use crate :: query:: RowStream ;
11
12
use crate :: simple_query:: SimpleQueryStream ;
12
13
#[ cfg( feature = "runtime" ) ]
@@ -20,11 +21,12 @@ use crate::{
20
21
CopyInSink , Error , Row , SimpleQueryMessage , Statement , ToStatement , Transaction ,
21
22
TransactionBuilder ,
22
23
} ;
23
- use bytes:: { Buf , BytesMut } ;
24
+ use bytes:: { Buf , BytesMut , BufMut } ;
24
25
use fallible_iterator:: FallibleIterator ;
25
26
use futures_channel:: mpsc;
26
27
use futures_util:: { future, pin_mut, ready, StreamExt , TryStreamExt } ;
27
28
use parking_lot:: Mutex ;
29
+ use crate :: statement:: Column ;
28
30
use postgres_protocol:: message:: { backend:: Message , frontend} ;
29
31
use postgres_types:: BorrowToSql ;
30
32
use std:: collections:: HashMap ;
@@ -374,6 +376,90 @@ impl Client {
374
376
query:: query ( & self . inner , statement, params) . await
375
377
}
376
378
379
+ /// Pass text directly to the Postgres backend to allow it to sort out typing itself and
380
+ /// to save a roundtrip
381
+ pub async fn query_raw_txt < ' a , S , I > ( & self , query : S , params : I ) -> Result < RowStream , Error >
382
+ where
383
+ S : AsRef < str > ,
384
+ I : IntoIterator < Item = S > ,
385
+ I :: IntoIter : ExactSizeIterator ,
386
+ {
387
+ let params = params. into_iter ( ) ;
388
+ let parems_len = params. len ( ) ;
389
+
390
+ let buf = self . inner . with_buf ( |buf| {
391
+ // Parse, anonymous portal
392
+ frontend:: parse ( "" , query. as_ref ( ) , std:: iter:: empty ( ) , buf) . map_err ( Error :: encode) ?;
393
+ // Bind, pass params as text, retrieve as binary
394
+ match frontend:: bind (
395
+ "" , // empty string selects the unnamed portal
396
+ "" , // empty string selects the unnamed prepared statement
397
+ std:: iter:: empty ( ) , // all parameters use the default format (text)
398
+ params,
399
+ |param, buf| {
400
+ buf. put_slice ( param. as_ref ( ) . as_bytes ( ) ) ;
401
+ Ok ( postgres_protocol:: IsNull :: No )
402
+ } ,
403
+ Some ( 1 ) , // all binary
404
+ buf,
405
+ ) {
406
+ Ok ( ( ) ) => Ok ( ( ) ) ,
407
+ Err ( frontend:: BindError :: Conversion ( e) ) => Err ( Error :: to_sql ( e, 0 ) ) , // TODO
408
+ Err ( frontend:: BindError :: Serialization ( e) ) => Err ( Error :: encode ( e) ) ,
409
+ } ?;
410
+
411
+ // Describe portal to typecast results
412
+ frontend:: describe ( b'P' , "" , buf) . map_err ( Error :: encode) ?;
413
+ // Execute
414
+ frontend:: execute ( "" , 0 , buf) . map_err ( Error :: encode) ?;
415
+ // Sync
416
+ frontend:: sync ( buf) ;
417
+
418
+ Ok ( buf. split ( ) . freeze ( ) )
419
+ } ) ?;
420
+
421
+ let mut responses = self . inner . send ( RequestMessages :: Single ( FrontendMessage :: Raw ( buf) ) ) ?;
422
+
423
+ // now read the responses
424
+
425
+ match responses. next ( ) . await ? {
426
+ Message :: ParseComplete => { }
427
+ _ => return Err ( Error :: unexpected_message ( ) ) ,
428
+ }
429
+ match responses. next ( ) . await ? {
430
+ Message :: BindComplete => { }
431
+ _ => return Err ( Error :: unexpected_message ( ) ) ,
432
+ }
433
+ let row_description = match responses. next ( ) . await ? {
434
+ Message :: RowDescription ( body) => Some ( body) ,
435
+ Message :: NoData => None ,
436
+ _ => return Err ( Error :: unexpected_message ( ) ) ,
437
+ } ;
438
+
439
+ // construct statement object
440
+
441
+ let parameters = vec ! [ Type :: TEXT ; parems_len] ;
442
+
443
+ let mut columns = vec ! [ ] ;
444
+ if let Some ( row_description) = row_description {
445
+ let mut it = row_description. fields ( ) ;
446
+ while let Some ( field) = it. next ( ) . map_err ( Error :: parse) ? {
447
+ let type_ = get_type ( & self . inner , field. type_oid ( ) ) . await ?;
448
+ let column = Column :: new ( field. name ( ) . to_string ( ) , type_) ;
449
+ columns. push ( column) ;
450
+ }
451
+ }
452
+
453
+ let statement = Statement :: new (
454
+ & self . inner ,
455
+ "" . to_owned ( ) ,
456
+ parameters,
457
+ columns
458
+ ) ;
459
+
460
+ Ok ( RowStream :: new ( statement, responses) )
461
+ }
462
+
377
463
/// Executes a statement, returning the number of rows modified.
378
464
///
379
465
/// A statement may contain parameters, specified by `$n`, where `n` is the index of the parameter of the list
0 commit comments