Add batch_execute

This commit is contained in:
Steven Fackler 2016-12-20 19:50:44 -08:00
parent 0c56a5ed13
commit 7edf66f7f0
10 changed files with 356 additions and 168 deletions

View File

@ -1,5 +1,3 @@
//! Error types.
use fallible_iterator::FallibleIterator; use fallible_iterator::FallibleIterator;
use postgres_protocol::message::backend::ErrorFields; use postgres_protocol::message::backend::ErrorFields;
use std::error; use std::error;
@ -141,7 +139,7 @@ pub struct DbError {
impl DbError { impl DbError {
#[doc(hidden)] #[doc(hidden)]
pub fn new_raw(fields: &mut ErrorFields) -> io::Result<DbError> { pub fn new(fields: &mut ErrorFields) -> io::Result<DbError> {
let mut severity = None; let mut severity = None;
let mut parsed_severity = None; let mut parsed_severity = None;
let mut code = None; let mut code = None;
@ -245,22 +243,6 @@ impl DbError {
_p: (), _p: (),
}) })
} }
#[doc(hidden)]
pub fn new_connect<T>(fields: &mut ErrorFields) -> Result<T, ConnectError> {
match DbError::new_raw(fields) {
Ok(err) => Err(ConnectError::Db(Box::new(err))),
Err(e) => Err(ConnectError::Io(e)),
}
}
#[doc(hidden)]
pub fn new<T>(fields: &mut ErrorFields) -> Result<T, Error> {
match DbError::new_raw(fields) {
Ok(err) => Err(Error::Db(Box::new(err))),
Err(e) => Err(Error::Io(e)),
}
}
} }
// manual impl to leave out _p // manual impl to leave out _p
@ -299,6 +281,20 @@ impl error::Error for DbError {
} }
} }
/// Represents the position of an error in a query.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ErrorPosition {
/// A position in the original query.
Normal(u32),
/// A position in an internally generated query.
Internal {
/// The byte position.
position: u32,
/// A query generated by the Postgres server.
query: String,
},
}
/// Reasons a new Postgres connection could fail. /// Reasons a new Postgres connection could fail.
#[derive(Debug)] #[derive(Debug)]
pub enum ConnectError { pub enum ConnectError {
@ -355,75 +351,3 @@ impl From<DbError> for ConnectError {
ConnectError::Db(Box::new(err)) ConnectError::Db(Box::new(err))
} }
} }
/// Represents the position of an error in a query.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ErrorPosition {
/// A position in the original query.
Normal(u32),
/// A position in an internally generated query.
Internal {
/// The byte position.
position: u32,
/// A query generated by the Postgres server.
query: String,
},
}
/// An error encountered when communicating with the Postgres server.
#[derive(Debug)]
pub enum Error {
/// An error reported by the Postgres server.
Db(Box<DbError>),
/// An error communicating with the Postgres server.
Io(io::Error),
/// An error converting between Postgres and Rust types.
Conversion(Box<error::Error + Sync + Send>),
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
try!(fmt.write_str(error::Error::description(self)));
match *self {
Error::Db(ref err) => write!(fmt, ": {}", err),
Error::Io(ref err) => write!(fmt, ": {}", err),
Error::Conversion(ref err) => write!(fmt, ": {}", err),
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::Db(_) => "Error reported by Postgres",
Error::Io(_) => "Error communicating with the server",
Error::Conversion(_) => "Error converting between Postgres and Rust types",
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
Error::Db(ref err) => Some(&**err),
Error::Io(ref err) => Some(err),
Error::Conversion(ref err) => Some(&**err),
}
}
}
impl From<DbError> for Error {
fn from(err: DbError) -> Error {
Error::Db(Box::new(err))
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
impl From<Error> for io::Error {
fn from(err: Error) -> io::Error {
io::Error::new(io::ErrorKind::Other, err)
}
}

View File

@ -5,5 +5,51 @@ extern crate fallible_iterator;
extern crate phf; extern crate phf;
extern crate postgres_protocol; extern crate postgres_protocol;
use fallible_iterator::{FallibleIterator, FromFallibleIterator};
use std::ops::Range;
pub mod error; pub mod error;
pub mod params; pub mod params;
pub struct RowData {
buf: Vec<u8>,
indices: Vec<Option<Range<usize>>>,
}
impl<'a> FromFallibleIterator<Option<&'a [u8]>> for RowData {
fn from_fallible_iterator<I>(mut it: I) -> Result<RowData, I::Error>
where I: FallibleIterator<Item = Option<&'a [u8]>>
{
let mut row = RowData {
buf: vec![],
indices: Vec::with_capacity(it.size_hint().0),
};
while let Some(cell) = try!(it.next()) {
let index = match cell {
Some(cell) => {
let base = row.buf.len();
row.buf.extend_from_slice(cell);
Some(base..row.buf.len())
}
None => None,
};
row.indices.push(index);
}
Ok(row)
}
}
impl RowData {
pub fn len(&self) -> usize {
self.indices.len()
}
pub fn get(&self, index: usize) -> Option<&[u8]> {
match &self.indices[index] {
&Some(ref range) => Some(&self.buf[range.clone()]),
&None => None,
}
}
}

View File

@ -4,6 +4,7 @@ version = "0.1.0"
authors = ["Steven Fackler <sfackler@gmail.com>"] authors = ["Steven Fackler <sfackler@gmail.com>"]
[dependencies] [dependencies]
fallible-iterator = "0.1.3"
futures = "0.1.7" futures = "0.1.7"
postgres-shared = { path = "../postgres-shared" } postgres-shared = { path = "../postgres-shared" }
postgres-protocol = "0.2" postgres-protocol = "0.2"

View File

@ -0,0 +1,50 @@
use std::error;
use std::io;
use std::fmt;
use Connection;
#[doc(inline)]
pub use postgres_shared::error::*;
#[derive(Debug)]
pub enum Error {
Io(io::Error),
Db(Box<DbError>, Connection),
Conversion(Box<error::Error + Sync + Send>, Connection),
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(error::Error::description(self))?;
match *self {
Error::Db(ref err, _) => write!(fmt, ": {}", err),
Error::Io(ref err) => write!(fmt, ": {}", err),
Error::Conversion(ref err, _) => write!(fmt, ": {}", err),
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::Db(_, _) => "Error reported by Postgres",
Error::Io(_) => "Error communicating with the server",
Error::Conversion(_, _) => "Error converting between Postgres and Rust types",
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
Error::Db(ref err, _) => Some(&**err),
Error::Io(ref err) => Some(err),
Error::Conversion(ref err, _) => Some(&**err),
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}

View File

@ -1,29 +1,31 @@
extern crate fallible_iterator;
extern crate futures;
extern crate postgres_shared; extern crate postgres_shared;
extern crate postgres_protocol; extern crate postgres_protocol;
extern crate tokio_core; extern crate tokio_core;
extern crate tokio_dns; extern crate tokio_dns;
extern crate tokio_uds; extern crate tokio_uds;
#[macro_use] use fallible_iterator::FallibleIterator;
extern crate futures;
use futures::{Future, IntoFuture, BoxFuture, Stream, Sink, Poll, StartSend}; use futures::{Future, IntoFuture, BoxFuture, Stream, Sink, Poll, StartSend};
use futures::future::Either; use futures::future::Either;
use postgres_protocol::authentication; use postgres_protocol::authentication;
use postgres_protocol::message::{backend, frontend}; use postgres_protocol::message::{backend, frontend};
use postgres_protocol::message::backend::ErrorFields;
use postgres_shared::RowData;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt;
use std::io; use std::io;
use tokio_core::reactor::Handle; use tokio_core::reactor::Handle;
#[doc(inline)]
pub use postgres_shared::error;
#[doc(inline)] #[doc(inline)]
pub use postgres_shared::params; pub use postgres_shared::params;
use error::{ConnectError, DbError}; use error::{ConnectError, Error, DbError};
use params::{ConnectParams, IntoConnectParams}; use params::{ConnectParams, IntoConnectParams};
use stream::PostgresStream; use stream::PostgresStream;
pub mod error;
mod stream; mod stream;
#[cfg(test)] #[cfg(test)]
@ -99,6 +101,14 @@ impl Sink for InnerConnection {
pub struct Connection(InnerConnection); pub struct Connection(InnerConnection);
// FIXME fill out
impl fmt::Debug for Connection {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Connection")
.finish()
}
}
impl Connection { impl Connection {
pub fn connect<T>(params: T, handle: &Handle) -> BoxFuture<Connection, ConnectError> pub fn connect<T>(params: T, handle: &Handle) -> BoxFuture<Connection, ConnectError>
where T: IntoConnectParams where T: IntoConnectParams
@ -184,9 +194,7 @@ impl Connection {
} }
} }
} }
backend::Message::ErrorResponse(body) => { backend::Message::ErrorResponse(body) => Err(connect_err(&mut body.fields())),
DbError::new_connect(&mut body.fields())
}
_ => Err(bad_message()), _ => Err(bad_message()),
}; };
@ -209,9 +217,7 @@ impl Connection {
.and_then(|(m, s)| { .and_then(|(m, s)| {
match m { match m {
backend::Message::AuthenticationOk => Ok(Connection(s)), backend::Message::AuthenticationOk => Ok(Connection(s)),
backend::Message::ErrorResponse(body) => { backend::Message::ErrorResponse(body) => Err(connect_err(&mut body.fields())),
DbError::new_connect(&mut body.fields())
}
_ => Err(bad_message()), _ => Err(bad_message()),
} }
}) })
@ -230,7 +236,7 @@ impl Connection {
} }
backend::Message::ReadyForQuery(_) => Either::B(Ok(Connection(s)).into_future()), backend::Message::ReadyForQuery(_) => Either::B(Ok(Connection(s)).into_future()),
backend::Message::ErrorResponse(body) => { backend::Message::ErrorResponse(body) => {
Either::B(DbError::new_connect(&mut body.fields()).into_future()) Either::B(Err(connect_err(&mut body.fields())).into_future())
} }
_ => Either::B(Err(bad_message()).into_future()), _ => Either::B(Err(bad_message()).into_future()),
} }
@ -238,11 +244,113 @@ impl Connection {
.boxed() .boxed()
} }
fn simple_query(self, query: &str) -> BoxFuture<(Vec<RowData>, Connection), Error> {
let mut buf = vec![];
frontend::query(query, &mut buf)
.map(|()| buf)
.into_future()
.and_then(move |buf| self.0.send(buf))
.and_then(|s| s.flush())
.map_err(Error::Io)
.and_then(|s| Connection(s).simple_read_rows(vec![]))
.boxed()
}
// This has its own read_rows since it will need to handle multiple query completions
fn simple_read_rows(self, mut rows: Vec<RowData>) -> BoxFuture<(Vec<RowData>, Connection), Error> {
self.0.read()
.map_err(|e| Error::Io(e.0))
.and_then(|(m, s)| {
match m {
backend::Message::ReadyForQuery(_) => {
Either::A(Ok((rows, Connection(s))).into_future())
}
backend::Message::DataRow(body) => {
match body.values().collect() {
Ok(row) => {
rows.push(row);
Either::B(Connection(s).simple_read_rows(rows))
}
Err(e) => Either::A(Err(Error::Io(e)).into_future()),
}
}
backend::Message::EmptyQueryResponse |
backend::Message::CommandComplete(_) => {
Either::B(Connection(s).simple_read_rows(rows))
}
backend::Message::ErrorResponse(body) => {
Either::A(Err(err(&mut body.fields(), Connection(s))).into_future())
}
_ => Either::A(Err(bad_message()).into_future()),
}
})
.boxed()
}
fn read_rows(self, mut rows: Vec<RowData>) -> BoxFuture<(Vec<RowData>, Connection), Error> {
self.0.read()
.map_err(|e| Error::Io(e.0))
.and_then(|(m, s)| {
match m {
backend::Message::EmptyQueryResponse |
backend::Message::CommandComplete(_) => {
Either::B(Connection(s).ready(rows))
},
backend::Message::DataRow(body) => {
match body.values().collect() {
Ok(row) => {
rows.push(row);
Either::B(Connection(s).read_rows(rows))
}
Err(e) => Either::A(Err(Error::Io(e)).into_future()),
}
}
backend::Message::ErrorResponse(body) => {
Either::A(Err(err(&mut body.fields(), Connection(s))).into_future())
}
_ => Either::A(Err(bad_message()).into_future()),
}
})
.boxed()
}
fn ready<T>(self, t: T) -> BoxFuture<(T, Connection), Error>
where T: 'static + Send
{
self.0.read()
.map_err(|e| Error::Io(e.0))
.and_then(|(m, s)| {
match m {
backend::Message::ReadyForQuery(_) => Ok((t, Connection(s))),
_ => Err(bad_message())
}
})
.boxed()
}
pub fn batch_execute(self, query: &str) -> BoxFuture<Connection, Error> {
self.simple_query(query).map(|r| r.1).boxed()
}
pub fn cancel_data(&self) -> CancelData { pub fn cancel_data(&self) -> CancelData {
self.0.cancel_data self.0.cancel_data
} }
} }
fn connect_err(fields: &mut ErrorFields) -> ConnectError {
match DbError::new(fields) {
Ok(err) => ConnectError::Db(Box::new(err)),
Err(err) => ConnectError::Io(err),
}
}
fn err(fields: &mut ErrorFields, conn: Connection) -> Error {
match DbError::new(fields) {
Ok(err) => Error::Db(Box::new(err), conn),
Err(err) => Error::Io(err),
}
}
fn bad_message<T>() -> T fn bad_message<T>() -> T
where T: From<io::Error> where T: From<io::Error>
{ {

View File

@ -1,7 +1,8 @@
use futures::Future;
use tokio_core::reactor::Core; use tokio_core::reactor::Core;
use super::*; use super::*;
use error::{ConnectError, SqlState}; use error::{Error, ConnectError, SqlState};
#[test] #[test]
fn basic() { fn basic() {
@ -75,3 +76,25 @@ fn pass_user_wrong_pass() {
Ok(_) => panic!("unexpected success"), Ok(_) => panic!("unexpected success"),
} }
} }
#[test]
fn batch_execute_ok() {
let mut l = Core::new().unwrap();
let done = Connection::connect("postgres://postgres@localhost", &l.handle())
.then(|c| c.unwrap().batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL);"));
l.run(done).unwrap();
}
#[test]
fn batch_execute_err() {
let mut l = Core::new().unwrap();
let done = Connection::connect("postgres://postgres@localhost", &l.handle())
.then(|r| r.unwrap().batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL); \
INSERT INTO foo DEFAULT VALUES;"))
.and_then(|c| c.batch_execute("SELECT * FROM bogo"));
match l.run(done) {
Err(Error::Db(ref e, _)) if e.code == SqlState::UndefinedTable => {}
Err(e) => panic!("unexpected error: {}", e),
Ok(_) => panic!("unexpected success"),
}
}

65
postgres/src/error.rs Normal file
View File

@ -0,0 +1,65 @@
//! Error types.
use std::fmt;
use std::io;
use std::error;
#[doc(inline)]
pub use postgres_shared::error::*;
/// An error encountered when communicating with the Postgres server.
#[derive(Debug)]
pub enum Error {
/// An error reported by the Postgres server.
Db(Box<DbError>),
/// An error communicating with the Postgres server.
Io(io::Error),
/// An error converting between Postgres and Rust types.
Conversion(Box<error::Error + Sync + Send>),
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
try!(fmt.write_str(error::Error::description(self)));
match *self {
Error::Db(ref err) => write!(fmt, ": {}", err),
Error::Io(ref err) => write!(fmt, ": {}", err),
Error::Conversion(ref err) => write!(fmt, ": {}", err),
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::Db(_) => "Error reported by Postgres",
Error::Io(_) => "Error communicating with the server",
Error::Conversion(_) => "Error converting between Postgres and Rust types",
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
Error::Db(ref err) => Some(&**err),
Error::Io(ref err) => Some(err),
Error::Conversion(ref err) => Some(&**err),
}
}
}
impl From<DbError> for Error {
fn from(err: DbError) -> Error {
Error::Db(Box::new(err))
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
impl From<Error> for io::Error {
fn from(err: Error) -> io::Error {
io::Error::new(io::ErrorKind::Other, err)
}
}

View File

@ -79,19 +79,19 @@ extern crate log;
extern crate postgres_protocol; extern crate postgres_protocol;
extern crate postgres_shared; extern crate postgres_shared;
use fallible_iterator::{FallibleIterator, FromFallibleIterator}; use fallible_iterator::FallibleIterator;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::collections::{VecDeque, HashMap}; use std::collections::{VecDeque, HashMap};
use std::fmt; use std::fmt;
use std::io; use std::io;
use std::mem; use std::mem;
use std::ops::Range;
use std::result; use std::result;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use postgres_protocol::authentication; use postgres_protocol::authentication;
use postgres_protocol::message::backend; use postgres_protocol::message::backend::{self, ErrorFields};
use postgres_protocol::message::frontend; use postgres_protocol::message::frontend;
use postgres_shared::RowData;
use error::{Error, ConnectError, SqlState, DbError}; use error::{Error, ConnectError, SqlState, DbError};
use tls::TlsHandshake; use tls::TlsHandshake;
@ -103,15 +103,13 @@ use stmt::{Statement, Column};
use transaction::{Transaction, IsolationLevel}; use transaction::{Transaction, IsolationLevel};
use types::{IsNull, Kind, Type, SessionInfo, Oid, Other, WrongType, ToSql, FromSql, Field}; use types::{IsNull, Kind, Type, SessionInfo, Oid, Other, WrongType, ToSql, FromSql, Field};
#[doc(inline)]
pub use postgres_shared::error;
#[macro_use] #[macro_use]
mod macros; mod macros;
mod feature_check; mod feature_check;
mod priv_io; mod priv_io;
mod url; mod url;
pub mod error;
pub mod tls; pub mod tls;
pub mod notification; pub mod notification;
pub mod params; pub mod params;
@ -317,7 +315,7 @@ impl InnerConnection {
} }
backend::Message::ReadyForQuery(_) => break, backend::Message::ReadyForQuery(_) => break,
backend::Message::ErrorResponse(body) => { backend::Message::ErrorResponse(body) => {
return DbError::new_connect(&mut body.fields()) return Err(connect_err(&mut body.fields()));
} }
_ => return Err(ConnectError::Io(bad_response())), _ => return Err(ConnectError::Io(bad_response())),
} }
@ -331,7 +329,7 @@ impl InnerConnection {
loop { loop {
match try_desync!(self, self.stream.read_message()) { match try_desync!(self, self.stream.read_message()) {
backend::Message::NoticeResponse(body) => { backend::Message::NoticeResponse(body) => {
if let Ok(err) = DbError::new_raw(&mut body.fields()) { if let Ok(err) = Err(err(&mut body.fields())) {
self.notice_handler.handle_notice(err); self.notice_handler.handle_notice(err);
} }
} }
@ -351,7 +349,7 @@ impl InnerConnection {
loop { loop {
match try_desync!(self, self.stream.read_message_timeout(timeout)) { match try_desync!(self, self.stream.read_message_timeout(timeout)) {
Some(backend::Message::NoticeResponse(body)) => { Some(backend::Message::NoticeResponse(body)) => {
if let Ok(err) = DbError::new_raw(&mut body.fields()) { if let Ok(err) = Err(err(&mut body.fields())) {
self.notice_handler.handle_notice(err); self.notice_handler.handle_notice(err);
} }
} }
@ -370,7 +368,7 @@ impl InnerConnection {
loop { loop {
match try_desync!(self, self.stream.read_message_nonblocking()) { match try_desync!(self, self.stream.read_message_nonblocking()) {
Some(backend::Message::NoticeResponse(body)) => { Some(backend::Message::NoticeResponse(body)) => {
if let Ok(err) = DbError::new_raw(&mut body.fields()) { if let Ok(err) = Err(err(&mut body.fields())) {
self.notice_handler.handle_notice(err); self.notice_handler.handle_notice(err);
} }
} }
@ -425,13 +423,13 @@ impl InnerConnection {
return Err(ConnectError::Io(io::Error::new(io::ErrorKind::Other, return Err(ConnectError::Io(io::Error::new(io::ErrorKind::Other,
"unsupported authentication"))) "unsupported authentication")))
} }
backend::Message::ErrorResponse(body) => return DbError::new_connect(&mut body.fields()), backend::Message::ErrorResponse(body) => return Err(connect_err(&mut body.fields())),
_ => return Err(ConnectError::Io(bad_response())), _ => return Err(ConnectError::Io(bad_response())),
} }
match try!(self.read_message()) { match try!(self.read_message()) {
backend::Message::AuthenticationOk => Ok(()), backend::Message::AuthenticationOk => Ok(()),
backend::Message::ErrorResponse(body) => DbError::new_connect(&mut body.fields()), backend::Message::ErrorResponse(body) => Err(connect_err(&mut body.fields())),
_ => Err(ConnectError::Io(bad_response())), _ => Err(ConnectError::Io(bad_response())),
} }
} }
@ -452,7 +450,7 @@ impl InnerConnection {
backend::Message::ParseComplete => {} backend::Message::ParseComplete => {}
backend::Message::ErrorResponse(body) => { backend::Message::ErrorResponse(body) => {
try!(self.wait_for_ready()); try!(self.wait_for_ready());
return DbError::new(&mut body.fields()); return Err(err(&mut body.fields()));
} }
_ => bad_response!(self), _ => bad_response!(self),
} }
@ -509,7 +507,7 @@ impl InnerConnection {
backend::Message::DataRow(body) => consumer(try!(body.values().collect())), backend::Message::DataRow(body) => consumer(try!(body.values().collect())),
backend::Message::ErrorResponse(body) => { backend::Message::ErrorResponse(body) => {
try!(self.wait_for_ready()); try!(self.wait_for_ready());
return DbError::new(&mut body.fields()); return Err(err(&mut body.fields()));
} }
backend::Message::CopyInResponse(_) => { backend::Message::CopyInResponse(_) => {
try!(self.stream.write_message(|buf| { try!(self.stream.write_message(|buf| {
@ -586,7 +584,7 @@ impl InnerConnection {
backend::Message::BindComplete => Ok(()), backend::Message::BindComplete => Ok(()),
backend::Message::ErrorResponse(body) => { backend::Message::ErrorResponse(body) => {
try!(self.wait_for_ready()); try!(self.wait_for_ready());
DbError::new(&mut body.fields()) Err(err(&mut body.fields()))
} }
_ => { _ => {
self.desynchronized = true; self.desynchronized = true;
@ -639,7 +637,7 @@ impl InnerConnection {
try!(self.stream.flush()); try!(self.stream.flush());
let resp = match try!(self.read_message()) { let resp = match try!(self.read_message()) {
backend::Message::CloseComplete => Ok(()), backend::Message::CloseComplete => Ok(()),
backend::Message::ErrorResponse(body) => DbError::new(&mut body.fields()), backend::Message::ErrorResponse(body) => Err(err(&mut body.fields())),
_ => bad_response!(self), _ => bad_response!(self),
}; };
try!(self.wait_for_ready()); try!(self.wait_for_ready());
@ -862,7 +860,7 @@ impl InnerConnection {
} }
backend::Message::ErrorResponse(body) => { backend::Message::ErrorResponse(body) => {
try!(self.wait_for_ready()); try!(self.wait_for_ready());
return DbError::new(&mut body.fields()); return Err(err(&mut body.fields()));
} }
_ => {} _ => {}
} }
@ -1328,46 +1326,17 @@ impl<'a> GenericConnection for Transaction<'a> {
} }
} }
struct RowData { fn connect_err(fields: &mut ErrorFields) -> ConnectError {
buf: Vec<u8>, match DbError::new(fields) {
indices: Vec<Option<Range<usize>>>, Ok(err) => ConnectError::Db(Box::new(err)),
} Err(err) => ConnectError::Io(err),
impl<'a> FromFallibleIterator<Option<&'a [u8]>> for RowData {
fn from_fallible_iterator<I>(mut it: I) -> result::Result<Self, I::Error>
where I: FallibleIterator<Item = Option<&'a [u8]>>
{
let mut row = RowData {
buf: vec![],
indices: Vec::with_capacity(it.size_hint().0),
};
while let Some(cell) = try!(it.next()) {
let index = match cell {
Some(cell) => {
let base = row.buf.len();
row.buf.extend_from_slice(cell);
Some(base..row.buf.len())
}
None => None,
};
row.indices.push(index);
}
Ok(row)
} }
} }
impl RowData { fn err(fields: &mut ErrorFields) -> Error {
fn len(&self) -> usize { match DbError::new(fields) {
self.indices.len() Ok(err) => Error::Db(Box::new(err)),
} Err(err) => Error::Io(err),
fn get(&self, index: usize) -> Option<&[u8]> {
match &self.indices[index] {
&Some(ref range) => Some(&self.buf[range.clone()]),
&None => None,
}
} }
} }

View File

@ -2,6 +2,7 @@
use fallible_iterator::FallibleIterator; use fallible_iterator::FallibleIterator;
use postgres_protocol::message::frontend; use postgres_protocol::message::frontend;
use postgres_shared::RowData;
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::fmt; use std::fmt;
@ -9,7 +10,7 @@ use std::io;
use std::ops::Deref; use std::ops::Deref;
use std::slice; use std::slice;
use {Result, SessionInfoNew, RowsNew, LazyRowsNew, StatementInternals, WrongTypeNew, RowData}; use {Result, SessionInfoNew, RowsNew, LazyRowsNew, StatementInternals, WrongTypeNew};
use transaction::Transaction; use transaction::Transaction;
use types::{FromSql, SessionInfo, WrongType}; use types::{FromSql, SessionInfo, WrongType};
use stmt::{Statement, Column}; use stmt::{Statement, Column};

View File

@ -7,12 +7,13 @@ use std::fmt;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::sync::Arc; use std::sync::Arc;
use postgres_protocol::message::{backend, frontend}; use postgres_protocol::message::{backend, frontend};
use postgres_shared::RowData;
use error::{Error, DbError}; use error::Error;
use types::{SessionInfo, Type, ToSql}; use types::{SessionInfo, Type, ToSql};
use rows::{Rows, LazyRows}; use rows::{Rows, LazyRows};
use transaction::Transaction; use transaction::Transaction;
use {bad_response, Connection, StatementInternals, Result, RowsNew, InnerConnection, RowData, use {bad_response, err, Connection, StatementInternals, Result, RowsNew, InnerConnection,
SessionInfoNew, LazyRowsNew, ColumnNew, StatementInfo, TransactionInternals}; SessionInfoNew, LazyRowsNew, ColumnNew, StatementInfo, TransactionInternals};
/// A prepared statement. /// A prepared statement.
@ -138,7 +139,7 @@ impl<'conn> Statement<'conn> {
backend::Message::DataRow(_) => {} backend::Message::DataRow(_) => {}
backend::Message::ErrorResponse(body) => { backend::Message::ErrorResponse(body) => {
try!(conn.wait_for_ready()); try!(conn.wait_for_ready());
return DbError::new(&mut body.fields()); return Err(err(&mut body.fields()));
} }
backend::Message::CommandComplete(body) => { backend::Message::CommandComplete(body) => {
num = parse_update_count(try!(body.tag())); num = parse_update_count(try!(body.tag()));
@ -162,7 +163,7 @@ impl<'conn> Statement<'conn> {
backend::Message::CopyDone => break, backend::Message::CopyDone => break,
backend::Message::ErrorResponse(body) => { backend::Message::ErrorResponse(body) => {
try!(conn.wait_for_ready()); try!(conn.wait_for_ready());
return DbError::new(&mut body.fields()); return Err(err(&mut body.fields()));
} }
_ => {} _ => {}
} }
@ -286,7 +287,7 @@ impl<'conn> Statement<'conn> {
} }
backend::Message::ErrorResponse(body) => { backend::Message::ErrorResponse(body) => {
try!(conn.wait_for_ready()); try!(conn.wait_for_ready());
return DbError::new(&mut body.fields()); return Err(err(&mut body.fields()));
} }
_ => { _ => {
loop { loop {
@ -347,7 +348,7 @@ impl<'conn> Statement<'conn> {
backend::Message::CommandComplete(body) => parse_update_count(try!(body.tag())), backend::Message::CommandComplete(body) => parse_update_count(try!(body.tag())),
backend::Message::ErrorResponse(body) => { backend::Message::ErrorResponse(body) => {
try!(info.conn.wait_for_ready()); try!(info.conn.wait_for_ready());
return DbError::new(&mut body.fields()); return Err(err(&mut body.fields()));
} }
_ => { _ => {
info.conn.desynchronized = true; info.conn.desynchronized = true;
@ -415,7 +416,7 @@ impl<'conn> Statement<'conn> {
} }
backend::Message::ErrorResponse(body) => { backend::Message::ErrorResponse(body) => {
try!(conn.wait_for_ready()); try!(conn.wait_for_ready());
return DbError::new(&mut body.fields()); return Err(err(&mut body.fields()));
} }
_ => { _ => {
loop { loop {
@ -462,7 +463,7 @@ impl<'conn> Statement<'conn> {
loop { loop {
if let backend::Message::ReadyForQuery(_) = if let backend::Message::ReadyForQuery(_) =
try!(info.conn.read_message()) { try!(info.conn.read_message()) {
return DbError::new(&mut body.fields()); return Err(err(&mut body.fields()));
} }
} }
} }