Remove mandatory failure from row lookup

This commit is contained in:
Steven Fackler 2014-03-29 17:01:41 -07:00
parent 4adf743aeb
commit 9ff7b3d00a
3 changed files with 83 additions and 54 deletions

View File

@ -519,25 +519,7 @@ pub enum PostgresError {
},
/// An attempt was made to convert between incompatible Rust and Postgres
/// types
PgWrongType(PostgresType)
}
impl PostgresError {
#[doc(hidden)]
pub fn pretty_error(&self, query: &str) -> ~str {
match *self {
PgDbError(ref err) => err.pretty_error(query),
PgStreamError(ref err) => format!("{}", *err),
PgStreamDesynchronized =>
~"The communication stream with the Postgres server has \
become desynchronized due to an earlier communications \
error",
PgWrongConnection =>
~"A statement was executed on a connection it was not \
prepared on ",
PgWrongParamCount { expected, actual } =>
format!("Expected {} parameters but got {}", expected, actual),
PgWrongType(ref ty) => format!("Unexpected type {}", *ty),
}
}
PgWrongType(PostgresType),
/// An attempt was made to read from a column that does not exist
PgInvalidColumn,
}

View File

@ -100,6 +100,7 @@ use error::{DnsError,
PgConnectDbError,
PgConnectStreamError,
PgDbError,
PgInvalidColumn,
PgStreamDesynchronized,
PgStreamError,
PgWrongParamCount,
@ -1332,24 +1333,30 @@ impl<'stmt> Iterator<PostgresRow<'stmt>> for PostgresResult<'stmt> {
}
/// A single result row of a query.
///
/// A value can be accessed by the name or index of its column, though access
/// by index is more efficient. Rows are 1-indexed.
///
/// ```rust,no_run
/// # use postgres::{PostgresConnection, NoSsl};
/// # let conn = PostgresConnection::connect("", &NoSsl).unwrap();
/// # let stmt = conn.prepare("").unwrap();
/// # let mut result = stmt.query([]).unwrap();
/// # let row = result.next().unwrap();
/// let foo: i32 = row[1];
/// let bar: ~str = row["bar"];
/// ```
pub struct PostgresRow<'stmt> {
priv stmt: &'stmt PostgresStatement<'stmt>,
priv data: Vec<Option<~[u8]>>
}
impl<'stmt> PostgresRow<'stmt> {
/// Retrieves the contents of a field of the row.
///
/// A field can be accessed by the name or index of its column, though
/// access by index is more efficient. Rows are 1-indexed.
///
/// Returns an `Error` value if the index does not reference a column or
/// the return type is not compatible with the Postgres type.
pub fn get<I: RowIndex, T: FromSql>(&self, idx: I)
-> Result<T, PostgresError> {
let idx = match idx.idx(self.stmt) {
Some(idx) => idx,
None => return Err(PgInvalidColumn)
};
FromSql::from_sql(&self.stmt.result_desc.get(idx).ty,
self.data.get(idx))
}
}
impl<'stmt> Container for PostgresRow<'stmt> {
#[inline]
fn len(&self) -> uint {
@ -1358,47 +1365,74 @@ impl<'stmt> Container for PostgresRow<'stmt> {
}
impl<'stmt, I: RowIndex, T: FromSql> Index<I, T> for PostgresRow<'stmt> {
/// Retreives the contents of a field of the row.
///
/// A field can be accessed by the name or index of its column, though
/// access by index is more efficient. Rows are 1-indexed.
///
/// # Failure
///
/// Fails if the index does not reference a column or the return type is
/// not compatible with the Postgres type.
///
/// # Example
///
/// ```rust,no_run
/// # use postgres::{PostgresConnection, NoSsl};
/// # let conn = PostgresConnection::connect("", &NoSsl).unwrap();
/// # let stmt = conn.prepare("").unwrap();
/// # let mut result = stmt.query([]).unwrap();
/// # let row = result.next().unwrap();
/// let foo: i32 = row[1];
/// let bar: ~str = row["bar"];
/// ```
fn index(&self, idx: &I) -> T {
let idx = idx.idx(self.stmt);
FromSql::from_sql(&self.stmt.result_desc.get(idx).ty, self.data.get(idx)).unwrap()
match self.get(idx.clone()) {
Ok(ok) => ok,
Err(err) => fail!("error retrieving row: {}", err)
}
}
}
/// A trait implemented by types that can index into columns of a row.
pub trait RowIndex {
/// Returns the index of the appropriate column.
///
/// # Failure
///
/// Fails if there is no corresponding column.
fn idx(&self, stmt: &PostgresStatement) -> uint;
pub trait RowIndex: Clone {
/// Returns the index of the appropriate column, or `None` if no such
/// column exists.
fn idx(&self, stmt: &PostgresStatement) -> Option<uint>;
}
impl RowIndex for uint {
#[inline]
fn idx(&self, _stmt: &PostgresStatement) -> uint {
assert!(*self != 0, "out of bounds row access");
*self - 1
fn idx(&self, stmt: &PostgresStatement) -> Option<uint> {
let idx = *self - 1;
if idx >= stmt.result_desc.len() {
None
} else {
Some(idx)
}
}
}
// This is a convenience as the 1 in get[1] resolves to int :(
impl RowIndex for int {
#[inline]
fn idx(&self, _stmt: &PostgresStatement) -> uint {
assert!(*self >= 1, "out of bounds row access");
(*self - 1) as uint
fn idx(&self, stmt: &PostgresStatement) -> Option<uint> {
if *self < 0 {
return None;
}
(*self as uint).idx(stmt)
}
}
impl<'a> RowIndex for &'a str {
fn idx(&self, stmt: &PostgresStatement) -> uint {
fn idx(&self, stmt: &PostgresStatement) -> Option<uint> {
for (i, desc) in stmt.result_descriptions().iter().enumerate() {
if desc.name.as_slice() == *self {
return i;
return Some(i);
}
}
fail!("there is no column with name {}", *self);
None
}
}

View File

@ -22,6 +22,7 @@ use error::{PgConnectDbError,
PgWrongConnection,
PgWrongParamCount,
PgWrongType,
PgInvalidColumn,
DnsError,
MissingPassword,
Position,
@ -739,7 +740,7 @@ fn test_too_many_params() {
}
#[test]
fn test_get_named() {
fn test_index_named() {
let conn = or_fail!(PostgresConnection::connect("postgres://postgres@localhost", &NoSsl));
let stmt = or_fail!(conn.prepare("SELECT 10::INT as val"));
let result = or_fail!(stmt.query([]));
@ -749,7 +750,7 @@ fn test_get_named() {
#[test]
#[should_fail]
fn test_get_named_fail() {
fn test_index_named_fail() {
let conn = or_fail!(PostgresConnection::connect("postgres://postgres@localhost", &NoSsl));
let stmt = or_fail!(conn.prepare("SELECT 10::INT as id"));
let mut result = or_fail!(stmt.query([]));
@ -757,6 +758,18 @@ fn test_get_named_fail() {
let _: i32 = result.next().unwrap()["asdf"];
}
#[test]
fn test_get_named_err() {
let conn = or_fail!(PostgresConnection::connect("postgres://postgres@localhost", &NoSsl));
let stmt = or_fail!(conn.prepare("SELECT 10::INT as id"));
let mut result = or_fail!(stmt.query([]));
match result.next().unwrap().get::<&str, i32>("asdf") {
Err(PgInvalidColumn) => {}
res => fail!("unexpected result {}", res),
};
}
#[test]
fn test_custom_notice_handler() {
static mut count: uint = 0;