Parsing of ErrorResponse and NoticeResponse

PostgresDbError::new plays a little fast and loose with the expected
format. Hopefully that's okay.

Closes #4
This commit is contained in:
Steven Fackler
2013-08-29 00:24:43 -04:00
parent b93bbd7eed
commit 30d1a43f4b
2 changed files with 93 additions and 26 deletions

View File

@@ -39,10 +39,8 @@ macro_rules! match_read_message(
)
fn handle_notice_response(fields: ~[(u8, ~str)]) {
// move_rev_iter is more efficient than move_iter
let map: HashMap<u8, ~str> = fields.move_rev_iter().collect();
info!("%s: %s", map.find(&('S' as u8)).unwrap().as_slice(),
map.find(&('M' as u8)).unwrap().as_slice());
let err = PostgresDbError::new(fields);
info!("%s: %s", err.severity, err.message);
}
pub struct PostgresConnection {
@@ -68,8 +66,53 @@ pub enum PostgresConnectError {
}
#[deriving(ToStr)]
// TODO this should have things in it
pub struct PostgresDbError;
pub enum PostgresErrorPosition {
Position(uint),
InternalPosition {
position: uint,
query: ~str
}
}
#[deriving(ToStr)]
pub struct PostgresDbError {
// This could almost be an enum, except the values can be localized :(
severity: ~str,
// Should probably end up as an enum
code: ~str,
message: ~str,
position: Option<PostgresErrorPosition>,
where: Option<~str>,
file: ~str,
line: uint,
routine: ~str
}
impl PostgresDbError {
fn new(fields: ~[(u8, ~str)]) -> PostgresDbError {
// move_rev_iter is more efficient than move_iter
let mut map: HashMap<u8, ~str> = fields.move_rev_iter().collect();
PostgresDbError {
severity: map.pop(&('S' as u8)).unwrap(),
code: map.pop(&('C' as u8)).unwrap(),
message: map.pop(&('M' as u8)).unwrap(),
position: match map.pop(&('P' as u8)) {
Some(pos) => Some(Position(FromStr::from_str(pos).unwrap())),
None => match map.pop(&('p' as u8)) {
Some(pos) => Some(InternalPosition {
position: FromStr::from_str(pos).unwrap(),
query: map.pop(&('q' as u8)).unwrap()
}),
None => None
}
},
where: map.pop(&('W' as u8)),
file: map.pop(&('F' as u8)).unwrap(),
line: FromStr::from_str(map.pop(&('L' as u8)).unwrap()).unwrap(),
routine: map.pop(&('R' as u8)).unwrap()
}
}
}
impl PostgresConnection {
pub fn connect(url: &str) -> PostgresConnection {
@@ -131,8 +174,8 @@ impl PostgresConnection {
match_read_message!(conn, {
ParameterStatus { parameter, value } =>
info!("Parameter %s = %s", parameter, value),
BackendKeyData { _ } => (),
ReadyForQuery { _ } => break,
BackendKeyData {_} => (),
ReadyForQuery {_} => break,
resp => fail!("Bad response: %?", resp.to_str())
})
}
@@ -185,7 +228,8 @@ impl PostgresConnection {
match_read_message!(self, {
AuthenticationOk => None,
ErrorResponse {_} => Some(DbError(PostgresDbError)),
ErrorResponse { fields } =>
Some(DbError(PostgresDbError::new(fields))),
resp => fail!("Bad response: %?", resp.to_str())
})
}
@@ -214,7 +258,8 @@ impl PostgresConnection {
match self.read_message() {
ParseComplete => (),
ErrorResponse {_} => return Err(PostgresDbError),
ErrorResponse { fields } =>
return Err(PostgresDbError::new(fields)),
resp => fail!("Bad response: %?", resp.to_str())
}
@@ -269,7 +314,8 @@ impl PostgresConnection {
loop {
match_read_message!(self, {
ReadyForQuery {_} => break,
resp @ ErrorResponse {_} => fail!("Error: %?", resp.to_str()),
ErrorResponse { fields } =>
fail!("Error: %s", PostgresDbError::new(fields).to_str()),
_ => ()
})
}
@@ -375,7 +421,7 @@ impl<'self> PostgresStatement<'self> {
match_read_message!(self.conn, {
BindComplete => None,
ErrorResponse {_} => Some(PostgresDbError),
ErrorResponse { fields } => Some(PostgresDbError::new(fields)),
resp => fail!("Bad response: %?", resp.to_str())
})
}
@@ -412,9 +458,9 @@ impl<'self> PostgresStatement<'self> {
DataRow {_} => (),
EmptyQueryResponse => break,
NoticeResponse {_} => (),
ErrorResponse {_} => {
ErrorResponse { fields } => {
self.conn.wait_for_ready();
return Err(PostgresDbError);
return Err(PostgresDbError::new(fields));
},
resp => fail!("Bad response: %?", resp.to_str())
})
@@ -452,9 +498,9 @@ impl<'self> PostgresStatement<'self> {
DataRow { row } => data.push(row),
CommandComplete {_} => break,
NoticeResponse {_} => (),
ErrorResponse {_} => {
ErrorResponse { fields } => {
self.conn.wait_for_ready();
return Err(PostgresDbError);
return Err(PostgresDbError::new(fields));
},
resp => fail!("Bad response: %?", resp.to_str())
})

View File

@@ -1,6 +1,6 @@
extern mod postgres;
use postgres::{PostgresConnection};
use postgres::*;
use postgres::types::ToSql;
#[test]
@@ -17,7 +17,16 @@ fn test_basic() {
#[test]
fn test_prepare_err() {
let conn = PostgresConnection::connect("postgres://postgres@127.0.0.1:5432");
assert!(conn.try_prepare("invalid sql statment").is_err());
match conn.try_prepare("invalid sql statment") {
Err(PostgresDbError { position, code, _ }) => {
assert_eq!(code, ~"42601");
match position {
Some(Position(1)) => (),
position => fail!("Unexpected position %?", position)
}
}
resp => fail!("Unexpected result %?", resp)
}
}
#[test]
@@ -65,15 +74,21 @@ fn test_plaintext_pass() {
}
#[test]
#[should_fail]
fn test_plaintext_pass_no_pass() {
PostgresConnection::connect("postgres://pass_user@127.0.0.1:5432");
let ret = PostgresConnection::try_connect("postgres://pass_user@127.0.0.1:5432");
match ret {
Err(MissingPassword) => (),
ret => fail!("Unexpected result %?", ret)
}
}
#[test]
#[should_fail]
fn test_plaintext_pass_wrong_pass() {
PostgresConnection::connect("postgres://pass_user:asdf@127.0.0.1:5432");
let ret = PostgresConnection::try_connect("postgres://pass_user:asdf@127.0.0.1:5432");
match ret {
Err(DbError(PostgresDbError { code, _ })) => assert_eq!(code, ~"28P01"),
ret => fail!("Unexpected result %?", ret)
}
}
#[test]
@@ -82,13 +97,19 @@ fn test_md5_pass() {
}
#[test]
#[should_fail]
fn test_md5_pass_no_pass() {
PostgresConnection::connect("postgres://md5_user@127.0.0.1:5432");
let ret = PostgresConnection::try_connect("postgres://md5_user@127.0.0.1:5432");
match ret {
Err(MissingPassword) => (),
ret => fail!("Unexpected result %?", ret)
}
}
#[test]
#[should_fail]
fn test_md5_pass_wrong_pass() {
PostgresConnection::connect("postgres://md5_user:asdf@127.0.0.1:5432");
let ret = PostgresConnection::try_connect("postgres://md5_user:asdf@127.0.0.1:5432");
match ret {
Err(DbError(PostgresDbError { code, _ })) => assert_eq!(code, ~"28P01"),
ret => fail!("Unexpected result %?", ret)
}
}