@@ -4,7 +4,7 @@ use debug_builders::DebugStruct;
4
4
use std:: cell:: { Cell , RefMut } ;
5
5
use std:: collections:: VecDeque ;
6
6
use std:: fmt;
7
- use std:: io:: { self , Cursor , BufRead , Read } ;
7
+ use std:: io:: { self , Read , Write } ;
8
8
9
9
use error:: { Error , DbError } ;
10
10
use types:: { SessionInfo , Type , ToSql , IsNull } ;
@@ -379,37 +379,29 @@ impl<'conn> Statement<'conn> {
379
379
Ok ( num)
380
380
}
381
381
382
- /// Executes a `COPY TO STDOUT` statement, returning a `Read`er of the
383
- /// resulting data .
382
+ /// Executes a `COPY TO STDOUT` statement, passing the resulting data to
383
+ /// the provided writer and returning the number of rows received .
384
384
///
385
385
/// See the [Postgres documentation](http://www.postgresql.org/docs/9.4/static/sql-copy.html)
386
386
/// for details on the data format.
387
387
///
388
388
/// If the statement is not a `COPY TO STDOUT` statement it will still be
389
389
/// executed and this method will return an error.
390
390
///
391
- /// # Warning
392
- ///
393
- /// The underlying connection may not be used while the returned `Read`er
394
- /// exists. Any attempt to do so will panic.
395
- ///
396
391
/// # Examples
397
392
///
398
393
/// ```rust,no_run
399
- /// # use std::io::Read;
400
394
/// # use postgres::{Connection, SslMode};
401
395
/// # let conn = Connection::connect("", &SslMode::None).unwrap();
402
396
/// conn.batch_execute("
403
397
/// CREATE TABLE people (id INT PRIMARY KEY, name VARCHAR);
404
398
/// INSERT INTO people (id, name) VALUES (1, 'john'), (2, 'jane');").unwrap();
405
399
/// let stmt = conn.prepare("COPY people TO STDOUT").unwrap();
406
- /// let mut r = stmt.copy_out(&[]).unwrap();
407
400
/// let mut buf = vec![];
408
- /// r.read_to_end(&mut buf).unwrap();
409
- /// r.finish().unwrap();
401
+ /// let mut r = stmt.copy_out(&[], &mut buf).unwrap();
410
402
/// assert_eq!(buf, b"1\tjohn\n2\tjane\n");
411
403
/// ```
412
- pub fn copy_out < ' a > ( & ' a self , params : & [ & ToSql ] ) -> Result < CopyOutReader < ' a > > {
404
+ pub fn copy_out < ' a , W : WriteWithInfo > ( & ' a self , params : & [ & ToSql ] , w : & mut W ) -> Result < u64 > {
413
405
try!( self . inner_execute ( "" , 0 , params) ) ;
414
406
let mut conn = self . conn . conn . borrow_mut ( ) ;
415
407
@@ -448,15 +440,57 @@ impl<'conn> Statement<'conn> {
448
440
}
449
441
} ;
450
442
451
- Ok ( CopyOutReader {
452
- info : CopyInfo {
453
- conn : conn,
454
- format : Format :: from_u16 ( format as u16 ) ,
455
- column_formats : column_formats. iter ( ) . map ( |& f| Format :: from_u16 ( f) ) . collect ( ) ,
456
- } ,
457
- buf : Cursor :: new ( vec ! [ ] ) ,
458
- finished : false ,
459
- } )
443
+ let mut info = CopyInfo {
444
+ conn : conn,
445
+ format : Format :: from_u16 ( format as u16 ) ,
446
+ column_formats : column_formats. iter ( ) . map ( |& f| Format :: from_u16 ( f) ) . collect ( ) ,
447
+ } ;
448
+
449
+ let count;
450
+ loop {
451
+ match try!( info. conn . read_message ( ) ) {
452
+ BCopyData { data } => {
453
+ let mut data = & data[ ..] ;
454
+ while !data. is_empty ( ) {
455
+ match w. write_with_info ( data, & info) {
456
+ Ok ( n) => data = & data[ n..] ,
457
+ Err ( e) => {
458
+ loop {
459
+ match try!( info. conn . read_message ( ) ) {
460
+ ReadyForQuery { .. } => return Err ( Error :: IoError ( e) ) ,
461
+ _ => { }
462
+ }
463
+ }
464
+ }
465
+ }
466
+ }
467
+ }
468
+ BCopyDone => { } ,
469
+ CommandComplete { tag } => {
470
+ count = util:: parse_update_count ( tag) ;
471
+ break ;
472
+ }
473
+ ErrorResponse { fields } => {
474
+ loop {
475
+ match try!( info. conn . read_message ( ) ) {
476
+ ReadyForQuery { .. } => return DbError :: new ( fields) ,
477
+ _ => { }
478
+ }
479
+ }
480
+ }
481
+ _ => {
482
+ loop {
483
+ match try!( info. conn . read_message ( ) ) {
484
+ ReadyForQuery { .. } => return Err ( Error :: IoError ( bad_response ( ) ) ) ,
485
+ _ => { }
486
+ }
487
+ }
488
+ }
489
+ }
490
+ }
491
+
492
+ try!( info. conn . wait_for_ready ( ) ) ;
493
+ Ok ( count)
460
494
}
461
495
462
496
/// Consumes the statement, clearing it from the Postgres session.
@@ -539,6 +573,20 @@ impl<R: Read> ReadWithInfo for R {
539
573
}
540
574
}
541
575
576
+ /// Like `Write` except that a `CopyInfo` object is provided as well.
577
+ ///
578
+ /// All types that implement `Write` also implement this trait.
579
+ pub trait WriteWithInfo {
580
+ /// Like `Write::write`.
581
+ fn write_with_info ( & mut self , buf : & [ u8 ] , info : & CopyInfo ) -> io:: Result < usize > ;
582
+ }
583
+
584
+ impl < W : Write > WriteWithInfo for W {
585
+ fn write_with_info ( & mut self , buf : & [ u8 ] , _: & CopyInfo ) -> io:: Result < usize > {
586
+ self . write ( buf)
587
+ }
588
+ }
589
+
542
590
impl Column {
543
591
/// The name of the column.
544
592
pub fn name ( & self ) -> & str {
@@ -568,95 +616,3 @@ impl Format {
568
616
}
569
617
}
570
618
}
571
-
572
- /// A `Read`er for data from `COPY TO STDOUT` queries.
573
- ///
574
- /// # Warning
575
- ///
576
- /// The underlying connection may not be used while a `CopyOutReader` exists.
577
- /// Any attempt to do so will panic.
578
- pub struct CopyOutReader < ' a > {
579
- info : CopyInfo < ' a > ,
580
- buf : Cursor < Vec < u8 > > ,
581
- finished : bool ,
582
- }
583
-
584
- impl < ' a > Drop for CopyOutReader < ' a > {
585
- fn drop ( & mut self ) {
586
- let _ = self . finish_inner ( ) ;
587
- }
588
- }
589
-
590
- impl < ' a > CopyOutReader < ' a > {
591
- /// Returns the `CopyInfo` for the current operation.
592
- pub fn info ( & self ) -> & CopyInfo {
593
- & self . info
594
- }
595
-
596
- /// Consumes the `CopyOutReader`, throwing away any unread data.
597
- ///
598
- /// Functionally equivalent to `CopyOutReader`'s `Drop` implementation,
599
- /// except that it returns any error encountered to the caller.
600
- pub fn finish ( mut self ) -> Result < ( ) > {
601
- self . finish_inner ( )
602
- }
603
-
604
- fn finish_inner ( & mut self ) -> Result < ( ) > {
605
- while !self . finished {
606
- let pos = self . buf . get_ref ( ) . len ( ) as u64 ;
607
- self . buf . set_position ( pos) ;
608
- try!( self . ensure_filled ( ) ) ;
609
- }
610
- Ok ( ( ) )
611
- }
612
-
613
- fn ensure_filled ( & mut self ) -> Result < ( ) > {
614
- if self . finished || self . buf . position ( ) != self . buf . get_ref ( ) . len ( ) as u64 {
615
- return Ok ( ( ) ) ;
616
- }
617
-
618
- match try!( self . info . conn . read_message ( ) ) {
619
- BCopyData { data } => self . buf = Cursor :: new ( data) ,
620
- BCopyDone => {
621
- self . finished = true ;
622
- match try!( self . info . conn . read_message ( ) ) {
623
- CommandComplete { .. } => { }
624
- _ => {
625
- self . info . conn . desynchronized = true ;
626
- return Err ( Error :: IoError ( bad_response ( ) ) ) ;
627
- }
628
- }
629
- try!( self . info . conn . wait_for_ready ( ) ) ;
630
- }
631
- ErrorResponse { fields } => {
632
- self . finished = true ;
633
- try!( self . info . conn . wait_for_ready ( ) ) ;
634
- return DbError :: new ( fields) ;
635
- }
636
- _ => {
637
- self . info . conn . desynchronized = true ;
638
- return Err ( Error :: IoError ( bad_response ( ) ) ) ;
639
- }
640
- }
641
-
642
- Ok ( ( ) )
643
- }
644
- }
645
-
646
- impl < ' a > Read for CopyOutReader < ' a > {
647
- fn read ( & mut self , buf : & mut [ u8 ] ) -> io:: Result < usize > {
648
- try!( self . ensure_filled ( ) ) ;
649
- self . buf . read ( buf)
650
- }
651
- }
652
-
653
- impl < ' a > BufRead for CopyOutReader < ' a > {
654
- fn fill_buf ( & mut self ) -> io:: Result < & [ u8 ] > {
655
- try!( self . ensure_filled ( ) ) ;
656
- self . buf . fill_buf ( )
657
- }
658
-
659
- fn consume ( & mut self , amt : usize ) {
660
- self . buf . consume ( amt)
661
- }
662
- }
0 commit comments