1+ //! Prepared statements
2+
13use debug_builders:: DebugStruct ;
2- use std:: cell:: Cell ;
4+ use std:: cell:: { Cell , RefMut } ;
35use std:: collections:: VecDeque ;
46use std:: fmt;
5- use std:: io;
7+ use std:: io:: { self , Cursor , BufRead , Read } ;
68
79use error:: { Error , DbError } ;
810use types:: { ReadWithInfo , SessionInfo , Type , ToSql , IsNull } ;
@@ -12,7 +14,7 @@ use message::WriteMessage;
1214use util;
1315use rows:: { Rows , LazyRows } ;
1416use { read_rows, bad_response, Connection , Transaction , StatementInternals , Result , RowsNew } ;
15- use { SessionInfoNew , LazyRowsNew , DbErrorNew , ColumnNew } ;
17+ use { InnerConnection , SessionInfoNew , LazyRowsNew , DbErrorNew , ColumnNew } ;
1618
1719/// A prepared statement.
1820pub struct Statement < ' conn > {
@@ -371,6 +373,84 @@ impl<'conn> Statement<'conn> {
371373 Ok ( num)
372374 }
373375
376+ /// Executes a `COPY TO STDOUT` statement, returning a `Read`er of the
377+ /// resulting data.
378+ ///
379+ /// See the [Postgres documentation](http://www.postgresql.org/docs/9.4/static/sql-copy.html)
380+ /// for details on the data format.
381+ ///
382+ /// If the statement is not a `COPY TO STDOUT` statement it will still be
383+ /// executed and this method will return an error.
384+ ///
385+ /// # Warning
386+ ///
387+ /// The underlying connection may not be used while the returned `Read`er
388+ /// exists. Any attempt to do so will panic.
389+ ///
390+ /// # Examples
391+ ///
392+ /// ```rust,no_run
393+ /// # use std::io::Read;
394+ /// # use postgres::{Connection, SslMode};
395+ /// # let conn = Connection::connect("", &SslMode::None).unwrap();
396+ /// conn.batch_execute("
397+ /// CREATE TABLE people (id INT PRIMARY KEY, name VARCHAR);
398+ /// INSERT INTO people (id, name) VALUES (1, 'john'), (2, 'jane');").unwrap();
399+ /// let stmt = conn.prepare("COPY people TO STDOUT").unwrap();
400+ /// let mut r = stmt.copy_out(&[]).unwrap();
401+ /// let mut buf = vec![];
402+ /// r.read_to_end(&mut buf).unwrap();
403+ /// r.finish().unwrap();
404+ /// assert_eq!(buf, b"1\tjohn\n2\tjane\n");
405+ /// ```
406+ pub fn copy_out < ' a > ( & ' a self , params : & [ & ToSql ] ) -> Result < CopyOutReader < ' a > > {
407+ try!( self . inner_execute ( "" , 0 , params) ) ;
408+ let mut conn = self . conn . conn . borrow_mut ( ) ;
409+
410+ let ( format, column_formats) = match try!( conn. read_message ( ) ) {
411+ CopyOutResponse { format, column_formats } => ( format, column_formats) ,
412+ CopyInResponse { .. } => {
413+ try!( conn. write_messages ( & [
414+ CopyFail {
415+ message : "" ,
416+ } ,
417+ CopyDone ,
418+ Sync ] ) ) ;
419+ match try!( conn. read_message ( ) ) {
420+ ErrorResponse { .. } => { /* expected from the CopyFail */ }
421+ _ => {
422+ conn. desynchronized = true ;
423+ return Err ( Error :: IoError ( bad_response ( ) ) ) ;
424+ }
425+ }
426+ try!( conn. wait_for_ready ( ) ) ;
427+ return Err ( Error :: IoError ( io:: Error :: new (
428+ io:: ErrorKind :: InvalidInput ,
429+ "called `copy_out` on a non-`COPY TO STDOUT` statement" ) ) ) ;
430+ }
431+ _ => {
432+ loop {
433+ match try!( conn. read_message ( ) ) {
434+ ReadyForQuery { .. } => {
435+ return Err ( Error :: IoError ( io:: Error :: new (
436+ io:: ErrorKind :: InvalidInput ,
437+ "called `copy_out` on a non-`COPY TO STDOUT` statement" ) ) ) ;
438+ }
439+ _ => { }
440+ }
441+ }
442+ }
443+ } ;
444+
445+ Ok ( CopyOutReader {
446+ conn : conn,
447+ format : Format :: from_u16 ( format as u16 ) ,
448+ column_formats : column_formats. iter ( ) . map ( |& f| Format :: from_u16 ( f) ) . collect ( ) ,
449+ buf : Cursor :: new ( vec ! [ ] ) ,
450+ finished : false ,
451+ } )
452+ }
453+
374454 /// Consumes the statement, clearing it from the Postgres session.
375455 ///
376456 /// If this statement was created via the `prepare_cached` method, `finish`
@@ -425,4 +505,119 @@ impl Column {
425505 }
426506}
427507
508+ /// The format of a portion of COPY query data.
509+ #[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
510+ pub enum Format {
511+ /// A text based format.
512+ Text ,
513+ /// A binary format.
514+ Binary ,
515+ }
516+
517+ impl Format {
518+ fn from_u16 ( value : u16 ) -> Format {
519+ match value {
520+ 0 => Format :: Text ,
521+ _ => Format :: Binary ,
522+ }
523+ }
524+ }
525+
526+ /// A `Read`er for data from `COPY TO STDOUT` queries.
527+ ///
528+ /// # Warning
529+ ///
530+ /// The underlying connection may not be used while a `CopyOutReader` exists.
531+ /// Any calls to the connection with panic.
532+ pub struct CopyOutReader < ' a > {
533+ conn : RefMut < ' a , InnerConnection > ,
534+ format : Format ,
535+ column_formats : Vec < Format > ,
536+ buf : Cursor < Vec < u8 > > ,
537+ finished : bool ,
538+ }
539+
540+ impl < ' a > Drop for CopyOutReader < ' a > {
541+ fn drop ( & mut self ) {
542+ let _ = self . finish_inner ( ) ;
543+ }
544+ }
545+
546+ impl < ' a > CopyOutReader < ' a > {
547+ /// Returns the format of the overall data.
548+ pub fn format ( & self ) -> Format {
549+ self . format
550+ }
551+
552+ /// Returns the format of the individual columns.
553+ pub fn column_formats ( & self ) -> & [ Format ] {
554+ & self . column_formats
555+ }
556+
557+ /// Consumes the `CopyOutReader`, throwing away any unread data.
558+ ///
559+ /// Functionally equivalent to `CopyOutReader`'s `Drop` implementation,
560+ /// except that it returns any error encountered to the caller.
561+ pub fn finish ( mut self ) -> Result < ( ) > {
562+ self . finish_inner ( )
563+ }
564+
565+ fn finish_inner ( & mut self ) -> Result < ( ) > {
566+ while !self . finished {
567+ let pos = self . buf . get_ref ( ) . len ( ) as u64 ;
568+ self . buf . set_position ( pos) ;
569+ try!( self . ensure_filled ( ) ) ;
570+ }
571+ Ok ( ( ) )
572+ }
573+
574+ fn ensure_filled ( & mut self ) -> Result < ( ) > {
575+ if self . finished || self . buf . position ( ) != self . buf . get_ref ( ) . len ( ) as u64 {
576+ return Ok ( ( ) ) ;
577+ }
428578
579+ match try!( self . conn . read_message ( ) ) {
580+ BCopyData { data } => self . buf = Cursor :: new ( data) ,
581+ BCopyDone => {
582+ self . finished = true ;
583+ match try!( self . conn . read_message ( ) ) {
584+ CommandComplete { .. } => { }
585+ _ => {
586+ self . conn . desynchronized = true ;
587+ return Err ( Error :: IoError ( bad_response ( ) ) ) ;
588+ }
589+ }
590+ try!( self . conn . wait_for_ready ( ) ) ;
591+ }
592+ ErrorResponse { fields } => {
593+ self . finished = true ;
594+ try!( self . conn . wait_for_ready ( ) ) ;
595+ return DbError :: new ( fields) ;
596+ }
597+ _ => {
598+ self . conn . desynchronized = true ;
599+ return Err ( Error :: IoError ( bad_response ( ) ) ) ;
600+ }
601+ }
602+
603+ Ok ( ( ) )
604+ }
605+ }
606+
607+ impl < ' a > Read for CopyOutReader < ' a > {
608+ fn read ( & mut self , buf : & mut [ u8 ] ) -> io:: Result < usize > {
609+ try!( self . ensure_filled ( ) ) ;
610+ self . buf . read ( buf)
611+ }
612+ }
613+
614+ impl < ' a > BufRead for CopyOutReader < ' a > {
615+ fn fill_buf ( & mut self ) -> io:: Result < & [ u8 ] > {
616+ try!( self . ensure_filled ( ) ) ;
617+ self . buf . fill_buf ( )
618+ }
619+
620+ fn consume ( & mut self , amt : usize ) {
621+ self . buf . consume ( amt)
622+ }
623+ }
0 commit comments