Skip to content

Commit 03ee761

Browse files
committed
Add support for binary copy in execution
Binary copy in usage needs to call into `ToSql::to_sql`, which needs a `SessionInfo`. This defines a `Read`-like trait that also passes an instance in. A blanket impl is provided for `R: Read` so this should be backwards compatible.
1 parent 5258013 commit 03ee761

File tree

2 files changed

+39
-10
lines changed

2 files changed

+39
-10
lines changed

src/lib.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ use std::path::PathBuf;
7373
use error::{Error, ConnectError, SqlState, DbError};
7474
use types::{ToSql, FromSql};
7575
use io::{StreamWrapper, NegotiateSsl};
76-
use types::{IsNull, Kind, Type, SessionInfo, Oid, Other};
76+
use types::{IsNull, Kind, Type, SessionInfo, Oid, Other, ReadWithInfo};
7777
use message::BackendMessage::*;
7878
use message::FrontendMessage::*;
7979
use message::{FrontendMessage, BackendMessage, RowDescriptionEntry};
@@ -1530,9 +1530,10 @@ impl<'conn> Statement<'conn> {
15301530
/// Executes a `COPY FROM STDIN` statement, returning the number of rows
15311531
/// added.
15321532
///
1533-
/// The contents of the provided `Read`er are passed to the Postgres server
1534-
/// verbatim; it is the caller's responsibility to ensure the data is in
1535-
/// the proper format. See the [Postgres documentation](http://www.postgresql.org/docs/9.4/static/sql-copy.html)
1533+
/// The data read out of the provided `Read`er are passed to the Postgres
1534+
/// server verbatim; it is the caller's responsibility to ensure the data
1535+
/// is in the proper format. See the
1536+
/// [Postgres documentation](http://www.postgresql.org/docs/9.4/static/sql-copy.html)
15361537
/// for details.
15371538
///
15381539
/// If the statement is not a `COPY FROM STDIN` statement, it will still be
@@ -1547,7 +1548,7 @@ impl<'conn> Statement<'conn> {
15471548
/// let stmt = conn.prepare("COPY people FROM STDIN").unwrap();
15481549
/// stmt.copy_in(&[], &mut "1\tjohn\n2\tjane\n".as_bytes()).unwrap();
15491550
/// ```
1550-
pub fn copy_in<R: Read>(&self, params: &[&ToSql], r: &mut R) -> Result<u64> {
1551+
pub fn copy_in<R: ReadWithInfo>(&self, params: &[&ToSql], r: &mut R) -> Result<u64> {
15511552
try!(self.inner_execute("", 0, params));
15521553
let mut conn = self.conn.conn.borrow_mut();
15531554

@@ -1567,16 +1568,15 @@ impl<'conn> Statement<'conn> {
15671568
}
15681569
}
15691570

1570-
let mut buf = vec![];
1571+
let mut buf = [0; 16 * 1024];
15711572
loop {
1572-
match r.take(16 * 1024).read_to_end(&mut buf) {
1573+
match fill_copy_buf(&mut buf, r, &SessionInfo::new(&conn)) {
15731574
Ok(0) => break,
1574-
Ok(_) => {
1575+
Ok(len) => {
15751576
try_desync!(conn, conn.stream.write_message(
15761577
&CopyData {
1577-
data: &buf,
1578+
data: &buf[..len],
15781579
}));
1579-
buf.clear();
15801580
}
15811581
Err(err) => {
15821582
try!(conn.write_messages(&[
@@ -1628,6 +1628,20 @@ impl<'conn> Statement<'conn> {
16281628
}
16291629
}
16301630

1631+
fn fill_copy_buf<R: ReadWithInfo>(buf: &mut [u8], r: &mut R, info: &SessionInfo)
1632+
-> std_io::Result<usize> {
1633+
let mut nread = 0;
1634+
while nread < buf.len() {
1635+
match r.read_with_info(&mut buf[nread..], info) {
1636+
Ok(0) => break,
1637+
Ok(n) => nread += n,
1638+
Err(ref e) if e.kind() == std_io::ErrorKind::Interrupted => {}
1639+
Err(e) => return Err(e),
1640+
}
1641+
}
1642+
Ok(nread)
1643+
}
1644+
16311645
/// Information about a column of the result of a query.
16321646
#[derive(PartialEq, Eq, Clone, Debug)]
16331647
pub struct Column {

src/types/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::collections::HashMap;
44
use std::error;
55
use std::fmt;
66
use std::io::prelude::*;
7+
use std::io;
78
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
89

910
pub use self::slice::Slice;
@@ -75,6 +76,20 @@ impl<'a> SessionInfo<'a> {
7576
}
7677
}
7778

79+
/// Like `Read` except that a `SessionInfo` object is provided as well.
80+
///
81+
/// All types that implement `Read` also implement this trait.
82+
pub trait ReadWithInfo {
83+
/// Like `Read::read`.
84+
fn read_with_info(&mut self, buf: &mut [u8], info: &SessionInfo) -> io::Result<usize>;
85+
}
86+
87+
impl<R: Read> ReadWithInfo for R {
88+
fn read_with_info(&mut self, buf: &mut [u8], _: &SessionInfo) -> io::Result<usize> {
89+
self.read(buf)
90+
}
91+
}
92+
7893
/// A Postgres OID.
7994
pub type Oid = u32;
8095

0 commit comments

Comments
 (0)