Update to protocol 0.2
This commit is contained in:
parent
8e5f28eb70
commit
9d53e677ff
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
target/
|
||||
target
|
||||
Cargo.lock
|
||||
.cargo/
|
||||
.cargo
|
||||
.idea
|
||||
*.iml
|
||||
.vscode
|
||||
|
@ -41,7 +41,7 @@ fallible-iterator = "0.1.3"
|
||||
hex = "0.2"
|
||||
log = "0.3"
|
||||
phf = "=0.7.20"
|
||||
postgres-protocol = "0.1"
|
||||
postgres-protocol = "0.2"
|
||||
bit-vec = { version = "0.4", optional = true }
|
||||
chrono = { version = "0.2.14", optional = true }
|
||||
eui48 = { version = "0.1", optional = true }
|
||||
|
@ -120,10 +120,10 @@ fn make_impl(codes: &[Code], file: &mut BufWriter<File>) {
|
||||
write!(file, r#"
|
||||
impl SqlState {{
|
||||
/// Creates a `SqlState` from its error code.
|
||||
pub fn from_code(s: String) -> SqlState {{
|
||||
match SQLSTATE_MAP.get(&*s) {{
|
||||
pub fn from_code(s: &str) -> SqlState {{
|
||||
match SQLSTATE_MAP.get(s) {{
|
||||
Some(state) => state.clone(),
|
||||
None => SqlState::Other(s),
|
||||
None => SqlState::Other(s.to_owned()),
|
||||
}}
|
||||
}}
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
//! Error types.
|
||||
|
||||
use fallible_iterator::FallibleIterator;
|
||||
use postgres_protocol::message::backend::ErrorFields;
|
||||
use std::error;
|
||||
use std::convert::From;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::result;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub use self::sqlstate::SqlState;
|
||||
use {Result, DbErrorNew};
|
||||
@ -85,52 +86,92 @@ pub struct DbError {
|
||||
}
|
||||
|
||||
impl DbErrorNew for DbError {
|
||||
fn new_raw(fields: Vec<(u8, String)>) -> result::Result<DbError, ()> {
|
||||
let mut map: HashMap<_, _> = fields.into_iter().collect();
|
||||
fn new_raw(fields: &mut ErrorFields) -> io::Result<DbError> {
|
||||
let mut severity = None;
|
||||
let mut code = None;
|
||||
let mut message = None;
|
||||
let mut detail = None;
|
||||
let mut hint = None;
|
||||
let mut normal_position = None;
|
||||
let mut internal_position = None;
|
||||
let mut internal_query = None;
|
||||
let mut where_ = None;
|
||||
let mut schema = None;
|
||||
let mut table = None;
|
||||
let mut column = None;
|
||||
let mut datatype = None;
|
||||
let mut constraint = None;
|
||||
let mut file = None;
|
||||
let mut line = None;
|
||||
let mut routine = None;
|
||||
|
||||
while let Some(field) = try!(fields.next()) {
|
||||
match field.type_() {
|
||||
b'S' => severity = Some(field.value().to_owned()),
|
||||
b'C' => code = Some(SqlState::from_code(field.value())),
|
||||
b'M' => message = Some(field.value().to_owned()),
|
||||
b'D' => detail = Some(field.value().to_owned()),
|
||||
b'H' => hint = Some(field.value().to_owned()),
|
||||
b'P' => normal_position = Some(try!(field.value().parse::<u32>().map_err(|_| ::bad_response()))),
|
||||
b'p' => internal_position = Some(try!(field.value().parse::<u32>().map_err(|_| ::bad_response()))),
|
||||
b'q' => internal_query = Some(field.value().to_owned()),
|
||||
b'W' => where_ = Some(field.value().to_owned()),
|
||||
b's' => schema = Some(field.value().to_owned()),
|
||||
b't' => table = Some(field.value().to_owned()),
|
||||
b'c' => column = Some(field.value().to_owned()),
|
||||
b'd' => datatype = Some(field.value().to_owned()),
|
||||
b'n' => constraint = Some(field.value().to_owned()),
|
||||
b'F' => file = Some(field.value().to_owned()),
|
||||
b'L' => line = Some(try!(field.value().parse::<u32>().map_err(|_| ::bad_response()))),
|
||||
b'R' => routine = Some(field.value().to_owned()),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(DbError {
|
||||
severity: try!(map.remove(&b'S').ok_or(())),
|
||||
code: SqlState::from_code(try!(map.remove(&b'C').ok_or(()))),
|
||||
message: try!(map.remove(&b'M').ok_or(())),
|
||||
detail: map.remove(&b'D'),
|
||||
hint: map.remove(&b'H'),
|
||||
position: match map.remove(&b'P') {
|
||||
Some(pos) => Some(ErrorPosition::Normal(try!(pos.parse().map_err(|_| ())))),
|
||||
severity: try!(severity.ok_or_else(|| ::bad_response())),
|
||||
code: try!(code.ok_or_else(|| ::bad_response())),
|
||||
message: try!(message.ok_or_else(|| ::bad_response())),
|
||||
detail: detail,
|
||||
hint: hint,
|
||||
position: match normal_position {
|
||||
Some(position) => Some(ErrorPosition::Normal(position)),
|
||||
None => {
|
||||
match map.remove(&b'p') {
|
||||
Some(pos) => {
|
||||
match internal_position {
|
||||
Some(position) => {
|
||||
Some(ErrorPosition::Internal {
|
||||
position: try!(pos.parse().map_err(|_| ())),
|
||||
query: try!(map.remove(&b'q').ok_or(())),
|
||||
position: position,
|
||||
query: try!(internal_query.ok_or_else(|| ::bad_response())),
|
||||
})
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
},
|
||||
where_: map.remove(&b'W'),
|
||||
schema: map.remove(&b's'),
|
||||
table: map.remove(&b't'),
|
||||
column: map.remove(&b'c'),
|
||||
datatype: map.remove(&b'd'),
|
||||
constraint: map.remove(&b'n'),
|
||||
file: map.remove(&b'F'),
|
||||
line: map.remove(&b'L').and_then(|l| l.parse().ok()),
|
||||
routine: map.remove(&b'R'),
|
||||
where_: where_,
|
||||
schema: schema,
|
||||
table: table,
|
||||
column: column,
|
||||
datatype: datatype,
|
||||
constraint: constraint,
|
||||
file: file,
|
||||
line: line,
|
||||
routine: routine,
|
||||
_p: (),
|
||||
})
|
||||
}
|
||||
|
||||
fn new_connect<T>(fields: Vec<(u8, String)>) -> result::Result<T, ConnectError> {
|
||||
fn new_connect<T>(fields: &mut ErrorFields) -> result::Result<T, ConnectError> {
|
||||
match DbError::new_raw(fields) {
|
||||
Ok(err) => Err(ConnectError::Db(Box::new(err))),
|
||||
Err(()) => Err(ConnectError::Io(::bad_response())),
|
||||
Err(e) => Err(ConnectError::Io(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn new<T>(fields: Vec<(u8, String)>) -> Result<T> {
|
||||
fn new<T>(fields: &mut ErrorFields) -> Result<T> {
|
||||
match DbError::new_raw(fields) {
|
||||
Ok(err) => Err(Error::Db(Box::new(err))),
|
||||
Err(()) => Err(Error::Io(::bad_response())),
|
||||
Err(e) => Err(Error::Io(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -782,10 +782,10 @@ static SQLSTATE_MAP: phf::Map<&'static str, SqlState> = ::phf::Map {
|
||||
|
||||
impl SqlState {
|
||||
/// Creates a `SqlState` from its error code.
|
||||
pub fn from_code(s: String) -> SqlState {
|
||||
match SQLSTATE_MAP.get(&*s) {
|
||||
pub fn from_code(s: &str) -> SqlState {
|
||||
match SQLSTATE_MAP.get(s) {
|
||||
Some(state) => state.clone(),
|
||||
None => SqlState::Other(s),
|
||||
None => SqlState::Other(s.to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
|
158
src/lib.rs
158
src/lib.rs
@ -79,6 +79,7 @@ extern crate log;
|
||||
extern crate phf;
|
||||
extern crate postgres_protocol;
|
||||
|
||||
use fallible_iterator::FallibleIterator;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{VecDeque, HashMap};
|
||||
use std::fmt;
|
||||
@ -88,7 +89,7 @@ use std::result;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use postgres_protocol::authentication;
|
||||
use postgres_protocol::message::backend::{self, RowDescriptionEntry};
|
||||
use postgres_protocol::message::backend::{self, ErrorFields};
|
||||
use postgres_protocol::message::frontend;
|
||||
|
||||
use error::{Error, ConnectError, SqlState, DbError};
|
||||
@ -307,12 +308,14 @@ impl InnerConnection {
|
||||
|
||||
loop {
|
||||
match try!(conn.read_message()) {
|
||||
backend::Message::BackendKeyData { process_id, secret_key } => {
|
||||
conn.cancel_data.process_id = process_id;
|
||||
conn.cancel_data.secret_key = secret_key;
|
||||
backend::Message::BackendKeyData(body) => {
|
||||
conn.cancel_data.process_id = body.process_id();
|
||||
conn.cancel_data.secret_key = body.secret_key();
|
||||
}
|
||||
backend::Message::ReadyForQuery(_) => break,
|
||||
backend::Message::ErrorResponse(body) => {
|
||||
return DbError::new_connect(&mut body.fields())
|
||||
}
|
||||
backend::Message::ReadyForQuery { .. } => break,
|
||||
backend::Message::ErrorResponse { fields } => return DbError::new_connect(fields),
|
||||
_ => return Err(ConnectError::Io(bad_response())),
|
||||
}
|
||||
}
|
||||
@ -320,17 +323,18 @@ impl InnerConnection {
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
fn read_message_with_notification(&mut self) -> io::Result<backend::Message> {
|
||||
fn read_message_with_notification(&mut self) -> io::Result<backend::Message<Vec<u8>>> {
|
||||
debug_assert!(!self.desynchronized);
|
||||
loop {
|
||||
match try_desync!(self, self.stream.read_message()) {
|
||||
backend::Message::NoticeResponse { fields } => {
|
||||
if let Ok(err) = DbError::new_raw(fields) {
|
||||
backend::Message::NoticeResponse(body) => {
|
||||
if let Ok(err) = DbError::new_raw(&mut body.fields()) {
|
||||
self.notice_handler.handle_notice(err);
|
||||
}
|
||||
}
|
||||
backend::Message::ParameterStatus { parameter, value } => {
|
||||
self.parameters.insert(parameter, value);
|
||||
backend::Message::ParameterStatus(body) => {
|
||||
self.parameters.insert(try!(body.name()).to_owned(),
|
||||
try!(body.value()).to_owned());
|
||||
}
|
||||
val => return Ok(val),
|
||||
}
|
||||
@ -339,17 +343,18 @@ impl InnerConnection {
|
||||
|
||||
fn read_message_with_notification_timeout(&mut self,
|
||||
timeout: Duration)
|
||||
-> io::Result<Option<backend::Message>> {
|
||||
-> io::Result<Option<backend::Message<Vec<u8>>>> {
|
||||
debug_assert!(!self.desynchronized);
|
||||
loop {
|
||||
match try_desync!(self, self.stream.read_message_timeout(timeout)) {
|
||||
Some(backend::Message::NoticeResponse { fields }) => {
|
||||
if let Ok(err) = DbError::new_raw(fields) {
|
||||
Some(backend::Message::NoticeResponse(body)) => {
|
||||
if let Ok(err) = DbError::new_raw(&mut body.fields()) {
|
||||
self.notice_handler.handle_notice(err);
|
||||
}
|
||||
}
|
||||
Some(backend::Message::ParameterStatus { parameter, value }) => {
|
||||
self.parameters.insert(parameter, value);
|
||||
Some(backend::Message::ParameterStatus(body)) => {
|
||||
self.parameters.insert(try!(body.name()).to_owned(),
|
||||
try!(body.value()).to_owned());
|
||||
}
|
||||
val => return Ok(val),
|
||||
}
|
||||
@ -357,31 +362,32 @@ impl InnerConnection {
|
||||
}
|
||||
|
||||
fn read_message_with_notification_nonblocking(&mut self)
|
||||
-> io::Result<Option<backend::Message>> {
|
||||
-> io::Result<Option<backend::Message<Vec<u8>>>> {
|
||||
debug_assert!(!self.desynchronized);
|
||||
loop {
|
||||
match try_desync!(self, self.stream.read_message_nonblocking()) {
|
||||
Some(backend::Message::NoticeResponse { fields }) => {
|
||||
if let Ok(err) = DbError::new_raw(fields) {
|
||||
Some(backend::Message::NoticeResponse(body)) => {
|
||||
if let Ok(err) = DbError::new_raw(&mut body.fields()) {
|
||||
self.notice_handler.handle_notice(err);
|
||||
}
|
||||
}
|
||||
Some(backend::Message::ParameterStatus { parameter, value }) => {
|
||||
self.parameters.insert(parameter, value);
|
||||
Some(backend::Message::ParameterStatus(body)) => {
|
||||
self.parameters.insert(try!(body.name()).to_owned(),
|
||||
try!(body.value()).to_owned());
|
||||
}
|
||||
val => return Ok(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_message(&mut self) -> io::Result<backend::Message> {
|
||||
fn read_message(&mut self) -> io::Result<backend::Message<Vec<u8>>> {
|
||||
loop {
|
||||
match try!(self.read_message_with_notification()) {
|
||||
backend::Message::NotificationResponse { process_id, channel, payload } => {
|
||||
backend::Message::NotificationResponse(body) => {
|
||||
self.notifications.push_back(Notification {
|
||||
process_id: process_id,
|
||||
channel: channel,
|
||||
payload: payload,
|
||||
process_id: body.process_id(),
|
||||
channel: try!(body.channel()).to_owned(),
|
||||
payload: try!(body.message()).to_owned(),
|
||||
})
|
||||
}
|
||||
val => return Ok(val),
|
||||
@ -399,28 +405,30 @@ impl InnerConnection {
|
||||
try!(self.stream.write_message(|buf| frontend::password_message(&pass, buf)));
|
||||
try!(self.stream.flush());
|
||||
}
|
||||
backend::Message::AuthenticationMD5Password { salt } => {
|
||||
backend::Message::AuthenticationMd5Password(body) => {
|
||||
let pass = try!(user.password.ok_or_else(|| {
|
||||
ConnectError::ConnectParams("a password was requested but not provided".into())
|
||||
}));
|
||||
let output = authentication::md5_hash(user.user.as_bytes(), pass.as_bytes(), salt);
|
||||
let output = authentication::md5_hash(user.user.as_bytes(),
|
||||
pass.as_bytes(),
|
||||
body.salt());
|
||||
try!(self.stream.write_message(|buf| frontend::password_message(&output, buf)));
|
||||
try!(self.stream.flush());
|
||||
}
|
||||
backend::Message::AuthenticationKerberosV5 |
|
||||
backend::Message::AuthenticationSCMCredential |
|
||||
backend::Message::AuthenticationGSS |
|
||||
backend::Message::AuthenticationSSPI => {
|
||||
backend::Message::AuthenticationScmCredential |
|
||||
backend::Message::AuthenticationGss |
|
||||
backend::Message::AuthenticationSspi => {
|
||||
return Err(ConnectError::Io(io::Error::new(io::ErrorKind::Other,
|
||||
"unsupported authentication")))
|
||||
}
|
||||
backend::Message::ErrorResponse { fields } => return DbError::new_connect(fields),
|
||||
backend::Message::ErrorResponse(body) => return DbError::new_connect(&mut body.fields()),
|
||||
_ => return Err(ConnectError::Io(bad_response())),
|
||||
}
|
||||
|
||||
match try!(self.read_message()) {
|
||||
backend::Message::AuthenticationOk => Ok(()),
|
||||
backend::Message::ErrorResponse { fields } => DbError::new_connect(fields),
|
||||
backend::Message::ErrorResponse(body) => DbError::new_connect(&mut body.fields()),
|
||||
_ => Err(ConnectError::Io(bad_response())),
|
||||
}
|
||||
}
|
||||
@ -439,35 +447,43 @@ impl InnerConnection {
|
||||
|
||||
match try!(self.read_message()) {
|
||||
backend::Message::ParseComplete => {}
|
||||
backend::Message::ErrorResponse { fields } => {
|
||||
backend::Message::ErrorResponse(body) => {
|
||||
try!(self.wait_for_ready());
|
||||
return DbError::new(fields);
|
||||
return DbError::new(&mut body.fields());
|
||||
}
|
||||
_ => bad_response!(self),
|
||||
}
|
||||
|
||||
let raw_param_types = match try!(self.read_message()) {
|
||||
backend::Message::ParameterDescription { types } => types,
|
||||
backend::Message::ParameterDescription(body) => body,
|
||||
_ => bad_response!(self),
|
||||
};
|
||||
|
||||
let raw_columns = match try!(self.read_message()) {
|
||||
backend::Message::RowDescription { descriptions } => descriptions,
|
||||
backend::Message::NoData => vec![],
|
||||
backend::Message::RowDescription(body) => Some(body),
|
||||
backend::Message::NoData => None,
|
||||
_ => bad_response!(self),
|
||||
};
|
||||
|
||||
try!(self.wait_for_ready());
|
||||
|
||||
let mut param_types = vec![];
|
||||
for oid in raw_param_types {
|
||||
param_types.push(try!(self.get_type(oid)));
|
||||
}
|
||||
|
||||
let mut columns = vec![];
|
||||
for RowDescriptionEntry { name, type_oid, .. } in raw_columns {
|
||||
columns.push(Column::new(name, try!(self.get_type(type_oid))));
|
||||
}
|
||||
let param_types = try!(raw_param_types
|
||||
.parameters()
|
||||
.map_err(Into::into)
|
||||
.and_then(|oid| self.get_type(oid))
|
||||
.collect());
|
||||
|
||||
let columns = match raw_columns {
|
||||
Some(body) => {
|
||||
try!(body.fields()
|
||||
.and_then(|field| {
|
||||
Ok(Column::new(field.name().to_owned(),
|
||||
try!(self.get_type(field.type_oid()))))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
Ok((param_types, columns))
|
||||
}
|
||||
@ -477,7 +493,7 @@ impl InnerConnection {
|
||||
loop {
|
||||
match try!(self.read_message()) {
|
||||
backend::Message::EmptyQueryResponse |
|
||||
backend::Message::CommandComplete { .. } => {
|
||||
backend::Message::CommandComplete(_) => {
|
||||
more_rows = false;
|
||||
break;
|
||||
}
|
||||
@ -485,12 +501,15 @@ impl InnerConnection {
|
||||
more_rows = true;
|
||||
break;
|
||||
}
|
||||
backend::Message::DataRow { row } => buf.push_back(row),
|
||||
backend::Message::ErrorResponse { fields } => {
|
||||
try!(self.wait_for_ready());
|
||||
return DbError::new(fields);
|
||||
backend::Message::DataRow(body) => {
|
||||
let row = try!(body.values().map(|v| v.map(ToOwned::to_owned)).collect());
|
||||
buf.push_back(row);
|
||||
}
|
||||
backend::Message::CopyInResponse { .. } => {
|
||||
backend::Message::ErrorResponse(body) => {
|
||||
try!(self.wait_for_ready());
|
||||
return DbError::new(&mut body.fields());
|
||||
}
|
||||
backend::Message::CopyInResponse(_) => {
|
||||
try!(self.stream.write_message(|buf| {
|
||||
frontend::copy_fail("COPY queries cannot be directly executed", buf)
|
||||
}));
|
||||
@ -498,9 +517,9 @@ impl InnerConnection {
|
||||
.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf))));
|
||||
try!(self.stream.flush());
|
||||
}
|
||||
backend::Message::CopyOutResponse { .. } => {
|
||||
backend::Message::CopyOutResponse(_) => {
|
||||
loop {
|
||||
if let backend::Message::ReadyForQuery { .. } = try!(self.read_message()) {
|
||||
if let backend::Message::ReadyForQuery(_) = try!(self.read_message()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -563,9 +582,9 @@ impl InnerConnection {
|
||||
|
||||
match try!(self.read_message()) {
|
||||
backend::Message::BindComplete => Ok(()),
|
||||
backend::Message::ErrorResponse { fields } => {
|
||||
backend::Message::ErrorResponse(body) => {
|
||||
try!(self.wait_for_ready());
|
||||
DbError::new(fields)
|
||||
DbError::new(&mut body.fields())
|
||||
}
|
||||
_ => {
|
||||
self.desynchronized = true;
|
||||
@ -618,7 +637,7 @@ impl InnerConnection {
|
||||
try!(self.stream.flush());
|
||||
let resp = match try!(self.read_message()) {
|
||||
backend::Message::CloseComplete => Ok(()),
|
||||
backend::Message::ErrorResponse { fields } => DbError::new(fields),
|
||||
backend::Message::ErrorResponse(body) => DbError::new(&mut body.fields()),
|
||||
_ => bad_response!(self),
|
||||
};
|
||||
try!(self.wait_for_ready());
|
||||
@ -813,7 +832,7 @@ impl InnerConnection {
|
||||
#[allow(needless_return)]
|
||||
fn wait_for_ready(&mut self) -> Result<()> {
|
||||
match try!(self.read_message()) {
|
||||
backend::Message::ReadyForQuery { .. } => Ok(()),
|
||||
backend::Message::ReadyForQuery(_) => Ok(()),
|
||||
_ => bad_response!(self),
|
||||
}
|
||||
}
|
||||
@ -827,13 +846,14 @@ impl InnerConnection {
|
||||
let mut result = vec![];
|
||||
loop {
|
||||
match try!(self.read_message()) {
|
||||
backend::Message::ReadyForQuery { .. } => break,
|
||||
backend::Message::DataRow { row } => {
|
||||
result.push(row.into_iter()
|
||||
.map(|opt| opt.map(|b| String::from_utf8_lossy(&b).into_owned()))
|
||||
backend::Message::ReadyForQuery(_) => break,
|
||||
backend::Message::DataRow(body) => {
|
||||
let row = try!(body.values()
|
||||
.map(|v| v.map(|v| String::from_utf8_lossy(v).into_owned()))
|
||||
.collect());
|
||||
result.push(row);
|
||||
}
|
||||
backend::Message::CopyInResponse { .. } => {
|
||||
backend::Message::CopyInResponse(_) => {
|
||||
try!(self.stream.write_message(|buf| {
|
||||
frontend::copy_fail("COPY queries cannot be directly executed", buf)
|
||||
}));
|
||||
@ -841,9 +861,9 @@ impl InnerConnection {
|
||||
.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf))));
|
||||
try!(self.stream.flush());
|
||||
}
|
||||
backend::Message::ErrorResponse { fields } => {
|
||||
backend::Message::ErrorResponse(body) => {
|
||||
try!(self.wait_for_ready());
|
||||
return DbError::new(fields);
|
||||
return DbError::new(&mut body.fields());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -1314,9 +1334,9 @@ trait OtherNew {
|
||||
}
|
||||
|
||||
trait DbErrorNew {
|
||||
fn new_raw(fields: Vec<(u8, String)>) -> result::Result<DbError, ()>;
|
||||
fn new_connect<T>(fields: Vec<(u8, String)>) -> result::Result<T, ConnectError>;
|
||||
fn new<T>(fields: Vec<(u8, String)>) -> Result<T>;
|
||||
fn new_raw(fields: &mut ErrorFields) -> io::Result<DbError>;
|
||||
fn new_connect<T>(fields: &mut ErrorFields) -> result::Result<T, ConnectError>;
|
||||
fn new<T>(fields: &mut ErrorFields) -> Result<T>;
|
||||
}
|
||||
|
||||
trait RowsNew<'a> {
|
||||
|
@ -113,11 +113,11 @@ impl<'a> FallibleIterator for Iter<'a> {
|
||||
}
|
||||
|
||||
match conn.read_message_with_notification_nonblocking() {
|
||||
Ok(Some(backend::Message::NotificationResponse { process_id, channel, payload })) => {
|
||||
Ok(Some(backend::Message::NotificationResponse(body))) => {
|
||||
Ok(Some(Notification {
|
||||
process_id: process_id,
|
||||
channel: channel,
|
||||
payload: payload,
|
||||
process_id: body.process_id(),
|
||||
channel: try!(body.channel()).to_owned(),
|
||||
payload: try!(body.message()).to_owned(),
|
||||
}))
|
||||
}
|
||||
Ok(None) => Ok(None),
|
||||
@ -152,11 +152,11 @@ impl<'a> FallibleIterator for BlockingIter<'a> {
|
||||
}
|
||||
|
||||
match conn.read_message_with_notification() {
|
||||
Ok(backend::Message::NotificationResponse { process_id, channel, payload }) => {
|
||||
Ok(backend::Message::NotificationResponse(body)) => {
|
||||
Ok(Some(Notification {
|
||||
process_id: process_id,
|
||||
channel: channel,
|
||||
payload: payload,
|
||||
process_id: body.process_id(),
|
||||
channel: try!(body.channel()).to_owned(),
|
||||
payload: try!(body.message()).to_owned(),
|
||||
}))
|
||||
}
|
||||
Err(err) => Err(Error::Io(err)),
|
||||
@ -188,11 +188,11 @@ impl<'a> FallibleIterator for TimeoutIter<'a> {
|
||||
}
|
||||
|
||||
match conn.read_message_with_notification_timeout(self.timeout) {
|
||||
Ok(Some(backend::Message::NotificationResponse { process_id, channel, payload })) => {
|
||||
Ok(Some(backend::Message::NotificationResponse(body))) => {
|
||||
Ok(Some(Notification {
|
||||
process_id: process_id,
|
||||
channel: channel,
|
||||
payload: payload,
|
||||
process_id: body.process_id(),
|
||||
channel: try!(body.channel()).to_owned(),
|
||||
payload: try!(body.message()).to_owned(),
|
||||
}))
|
||||
}
|
||||
Ok(None) => Ok(None),
|
||||
|
@ -47,12 +47,12 @@ impl MessageStream {
|
||||
self.stream.write_all(&self.buf).map_err(From::from)
|
||||
}
|
||||
|
||||
fn inner_read_message(&mut self, b: u8) -> io::Result<backend::Message> {
|
||||
fn inner_read_message(&mut self, b: u8) -> io::Result<backend::Message<Vec<u8>>> {
|
||||
self.buf.resize(MESSAGE_HEADER_SIZE, 0);
|
||||
self.buf[0] = b;
|
||||
try!(self.stream.read_exact(&mut self.buf[1..]));
|
||||
|
||||
let len = match try!(backend::Message::parse(&self.buf)) {
|
||||
let len = match try!(backend::Message::parse_owned(&self.buf)) {
|
||||
ParseResult::Complete { message, .. } => return Ok(message),
|
||||
ParseResult::Incomplete { required_size } => Some(required_size.unwrap()),
|
||||
};
|
||||
@ -62,13 +62,13 @@ impl MessageStream {
|
||||
try!(self.stream.read_exact(&mut self.buf[MESSAGE_HEADER_SIZE..]));
|
||||
};
|
||||
|
||||
match try!(backend::Message::parse(&self.buf)) {
|
||||
match try!(backend::Message::parse_owned(&self.buf)) {
|
||||
ParseResult::Complete { message, .. } => Ok(message),
|
||||
ParseResult::Incomplete { .. } => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_message(&mut self) -> io::Result<backend::Message> {
|
||||
pub fn read_message(&mut self) -> io::Result<backend::Message<Vec<u8>>> {
|
||||
let mut b = [0; 1];
|
||||
try!(self.stream.read_exact(&mut b));
|
||||
self.inner_read_message(b[0])
|
||||
@ -76,7 +76,7 @@ impl MessageStream {
|
||||
|
||||
pub fn read_message_timeout(&mut self,
|
||||
timeout: Duration)
|
||||
-> io::Result<Option<backend::Message>> {
|
||||
-> io::Result<Option<backend::Message<Vec<u8>>>> {
|
||||
try!(self.set_read_timeout(Some(timeout)));
|
||||
let mut b = [0; 1];
|
||||
let r = self.stream.read_exact(&mut b);
|
||||
@ -90,7 +90,7 @@ impl MessageStream {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_message_nonblocking(&mut self) -> io::Result<Option<backend::Message>> {
|
||||
pub fn read_message_nonblocking(&mut self) -> io::Result<Option<backend::Message<Vec<u8>>>> {
|
||||
try!(self.set_nonblocking(true));
|
||||
let mut b = [0; 1];
|
||||
let r = self.stream.read_exact(&mut b);
|
||||
|
81
src/stmt.rs
81
src/stmt.rs
@ -1,5 +1,6 @@
|
||||
//! Prepared statements
|
||||
|
||||
use fallible_iterator::FallibleIterator;
|
||||
use std::cell::{Cell, RefMut};
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
@ -132,20 +133,20 @@ impl<'conn> Statement<'conn> {
|
||||
let num;
|
||||
loop {
|
||||
match try!(conn.read_message()) {
|
||||
backend::Message::DataRow { .. } => {}
|
||||
backend::Message::ErrorResponse { fields } => {
|
||||
backend::Message::DataRow(_) => {}
|
||||
backend::Message::ErrorResponse(body) => {
|
||||
try!(conn.wait_for_ready());
|
||||
return DbError::new(fields);
|
||||
return DbError::new(&mut body.fields());
|
||||
}
|
||||
backend::Message::CommandComplete { tag } => {
|
||||
num = parse_update_count(tag);
|
||||
backend::Message::CommandComplete(body) => {
|
||||
num = parse_update_count(try!(body.tag()));
|
||||
break;
|
||||
}
|
||||
backend::Message::EmptyQueryResponse => {
|
||||
num = 0;
|
||||
break;
|
||||
}
|
||||
backend::Message::CopyInResponse { .. } => {
|
||||
backend::Message::CopyInResponse(_) => {
|
||||
try!(conn.stream.write_message(|buf| {
|
||||
frontend::copy_fail("COPY queries cannot be directly executed", buf)
|
||||
}));
|
||||
@ -153,13 +154,13 @@ impl<'conn> Statement<'conn> {
|
||||
.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf))));
|
||||
try!(conn.stream.flush());
|
||||
}
|
||||
backend::Message::CopyOutResponse { .. } => {
|
||||
backend::Message::CopyOutResponse(_) => {
|
||||
loop {
|
||||
match try!(conn.read_message()) {
|
||||
backend::Message::CopyDone => break,
|
||||
backend::Message::ErrorResponse { fields } => {
|
||||
backend::Message::ErrorResponse(body) => {
|
||||
try!(conn.wait_for_ready());
|
||||
return DbError::new(fields);
|
||||
return DbError::new(&mut body.fields());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -269,14 +270,20 @@ impl<'conn> Statement<'conn> {
|
||||
try!(conn.raw_execute(&self.info.name, "", 0, self.param_types(), params));
|
||||
|
||||
let (format, column_formats) = match try!(conn.read_message()) {
|
||||
backend::Message::CopyInResponse { format, column_formats } => (format, column_formats),
|
||||
backend::Message::ErrorResponse { fields } => {
|
||||
backend::Message::CopyInResponse(body) => {
|
||||
let format = body.format();
|
||||
let column_formats = try!(body.column_formats()
|
||||
.map(|f| Format::from_u16(f))
|
||||
.collect());
|
||||
(format, column_formats)
|
||||
}
|
||||
backend::Message::ErrorResponse(body) => {
|
||||
try!(conn.wait_for_ready());
|
||||
return DbError::new(fields);
|
||||
return DbError::new(&mut body.fields());
|
||||
}
|
||||
_ => {
|
||||
loop {
|
||||
if let backend::Message::ReadyForQuery { .. } = try!(conn.read_message()) {
|
||||
if let backend::Message::ReadyForQuery(_) = try!(conn.read_message()) {
|
||||
return Err(Error::Io(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"called `copy_in` on a \
|
||||
non-`COPY FROM STDIN` \
|
||||
@ -289,7 +296,7 @@ impl<'conn> Statement<'conn> {
|
||||
let mut info = CopyInfo {
|
||||
conn: conn,
|
||||
format: Format::from_u16(format as u16),
|
||||
column_formats: column_formats.iter().map(|&f| Format::from_u16(f)).collect(),
|
||||
column_formats: column_formats,
|
||||
};
|
||||
|
||||
let mut buf = [0; 16 * 1024];
|
||||
@ -311,7 +318,7 @@ impl<'conn> Statement<'conn> {
|
||||
.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf))));
|
||||
try!(info.conn.stream.flush());
|
||||
match try!(info.conn.read_message()) {
|
||||
backend::Message::ErrorResponse { .. } => {
|
||||
backend::Message::ErrorResponse(_) => {
|
||||
// expected from the CopyFail
|
||||
}
|
||||
_ => {
|
||||
@ -330,10 +337,10 @@ impl<'conn> Statement<'conn> {
|
||||
try!(info.conn.stream.flush());
|
||||
|
||||
let num = match try!(info.conn.read_message()) {
|
||||
backend::Message::CommandComplete { tag } => parse_update_count(tag),
|
||||
backend::Message::ErrorResponse { fields } => {
|
||||
backend::Message::CommandComplete(body) => parse_update_count(try!(body.tag())),
|
||||
backend::Message::ErrorResponse(body) => {
|
||||
try!(info.conn.wait_for_ready());
|
||||
return DbError::new(fields);
|
||||
return DbError::new(&mut body.fields());
|
||||
}
|
||||
_ => {
|
||||
info.conn.desynchronized = true;
|
||||
@ -372,17 +379,21 @@ impl<'conn> Statement<'conn> {
|
||||
try!(conn.raw_execute(&self.info.name, "", 0, self.param_types(), params));
|
||||
|
||||
let (format, column_formats) = match try!(conn.read_message()) {
|
||||
backend::Message::CopyOutResponse { format, column_formats } => {
|
||||
backend::Message::CopyOutResponse(body) => {
|
||||
let format = body.format();
|
||||
let column_formats = try!(body.column_formats()
|
||||
.map(|f| Format::from_u16(f))
|
||||
.collect());
|
||||
(format, column_formats)
|
||||
}
|
||||
backend::Message::CopyInResponse { .. } => {
|
||||
backend::Message::CopyInResponse(_) => {
|
||||
try!(conn.stream.write_message(|buf| frontend::copy_fail("", buf)));
|
||||
try!(conn.stream
|
||||
.write_message(|buf| Ok::<(), io::Error>(frontend::copy_done(buf))));
|
||||
try!(conn.stream.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf))));
|
||||
try!(conn.stream.flush());
|
||||
match try!(conn.read_message()) {
|
||||
backend::Message::ErrorResponse { .. } => {
|
||||
backend::Message::ErrorResponse(_) => {
|
||||
// expected from the CopyFail
|
||||
}
|
||||
_ => {
|
||||
@ -395,13 +406,13 @@ impl<'conn> Statement<'conn> {
|
||||
"called `copy_out` on a non-`COPY TO \
|
||||
STDOUT` statement")));
|
||||
}
|
||||
backend::Message::ErrorResponse { fields } => {
|
||||
backend::Message::ErrorResponse(body) => {
|
||||
try!(conn.wait_for_ready());
|
||||
return DbError::new(fields);
|
||||
return DbError::new(&mut body.fields());
|
||||
}
|
||||
_ => {
|
||||
loop {
|
||||
if let backend::Message::ReadyForQuery { .. } = try!(conn.read_message()) {
|
||||
if let backend::Message::ReadyForQuery(_) = try!(conn.read_message()) {
|
||||
return Err(Error::Io(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"called `copy_out` on a \
|
||||
non-`COPY TO STDOUT` statement")));
|
||||
@ -413,20 +424,20 @@ impl<'conn> Statement<'conn> {
|
||||
let mut info = CopyInfo {
|
||||
conn: conn,
|
||||
format: Format::from_u16(format as u16),
|
||||
column_formats: column_formats.iter().map(|&f| Format::from_u16(f)).collect(),
|
||||
column_formats: column_formats,
|
||||
};
|
||||
|
||||
let count;
|
||||
loop {
|
||||
match try!(info.conn.read_message()) {
|
||||
backend::Message::CopyData { data } => {
|
||||
let mut data = &data[..];
|
||||
backend::Message::CopyData(body) => {
|
||||
let mut data = body.data();
|
||||
while !data.is_empty() {
|
||||
match w.write_with_info(data, &info) {
|
||||
Ok(n) => data = &data[n..],
|
||||
Err(e) => {
|
||||
loop {
|
||||
if let backend::Message::ReadyForQuery { .. } =
|
||||
if let backend::Message::ReadyForQuery(_) =
|
||||
try!(info.conn.read_message()) {
|
||||
return Err(Error::Io(e));
|
||||
}
|
||||
@ -436,21 +447,21 @@ impl<'conn> Statement<'conn> {
|
||||
}
|
||||
}
|
||||
backend::Message::CopyDone => {}
|
||||
backend::Message::CommandComplete { tag } => {
|
||||
count = parse_update_count(tag);
|
||||
backend::Message::CommandComplete(body) => {
|
||||
count = parse_update_count(try!(body.tag()));
|
||||
break;
|
||||
}
|
||||
backend::Message::ErrorResponse { fields } => {
|
||||
backend::Message::ErrorResponse(body) => {
|
||||
loop {
|
||||
if let backend::Message::ReadyForQuery { .. } =
|
||||
if let backend::Message::ReadyForQuery(_) =
|
||||
try!(info.conn.read_message()) {
|
||||
return DbError::new(fields);
|
||||
return DbError::new(&mut body.fields());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
loop {
|
||||
if let backend::Message::ReadyForQuery { .. } =
|
||||
if let backend::Message::ReadyForQuery(_) =
|
||||
try!(info.conn.read_message()) {
|
||||
return Err(Error::Io(bad_response()));
|
||||
}
|
||||
@ -586,6 +597,6 @@ impl Format {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_update_count(tag: String) -> u64 {
|
||||
fn parse_update_count(tag: &str) -> u64 {
|
||||
tag.split(' ').last().unwrap().parse().unwrap_or(0)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user