Skip to content

Commit 9ff7b3d

Browse files
committed
Remove mandatory failure from row lookup
1 parent 4adf743 commit 9ff7b3d

File tree

3 files changed

+83
-54
lines changed

3 files changed

+83
-54
lines changed

src/error.rs

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -519,25 +519,7 @@ pub enum PostgresError {
519519
},
520520
/// An attempt was made to convert between incompatible Rust and Postgres
521521
/// types
522-
PgWrongType(PostgresType)
523-
}
524-
525-
impl PostgresError {
526-
#[doc(hidden)]
527-
pub fn pretty_error(&self, query: &str) -> ~str {
528-
match *self {
529-
PgDbError(ref err) => err.pretty_error(query),
530-
PgStreamError(ref err) => format!("{}", *err),
531-
PgStreamDesynchronized =>
532-
~"The communication stream with the Postgres server has \
533-
become desynchronized due to an earlier communications \
534-
error",
535-
PgWrongConnection =>
536-
~"A statement was executed on a connection it was not \
537-
prepared on ",
538-
PgWrongParamCount { expected, actual } =>
539-
format!("Expected {} parameters but got {}", expected, actual),
540-
PgWrongType(ref ty) => format!("Unexpected type {}", *ty),
541-
}
542-
}
522+
PgWrongType(PostgresType),
523+
/// An attempt was made to read from a column that does not exist
524+
PgInvalidColumn,
543525
}

src/lib.rs

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ use error::{DnsError,
100100
PgConnectDbError,
101101
PgConnectStreamError,
102102
PgDbError,
103+
PgInvalidColumn,
103104
PgStreamDesynchronized,
104105
PgStreamError,
105106
PgWrongParamCount,
@@ -1332,24 +1333,30 @@ impl<'stmt> Iterator<PostgresRow<'stmt>> for PostgresResult<'stmt> {
13321333
}
13331334

13341335
/// A single result row of a query.
1335-
///
1336-
/// A value can be accessed by the name or index of its column, though access
1337-
/// by index is more efficient. Rows are 1-indexed.
1338-
///
1339-
/// ```rust,no_run
1340-
/// # use postgres::{PostgresConnection, NoSsl};
1341-
/// # let conn = PostgresConnection::connect("", &NoSsl).unwrap();
1342-
/// # let stmt = conn.prepare("").unwrap();
1343-
/// # let mut result = stmt.query([]).unwrap();
1344-
/// # let row = result.next().unwrap();
1345-
/// let foo: i32 = row[1];
1346-
/// let bar: ~str = row["bar"];
1347-
/// ```
13481336
pub struct PostgresRow<'stmt> {
13491337
priv stmt: &'stmt PostgresStatement<'stmt>,
13501338
priv data: Vec<Option<~[u8]>>
13511339
}
13521340

1341+
impl<'stmt> PostgresRow<'stmt> {
1342+
/// Retrieves the contents of a field of the row.
1343+
///
1344+
/// A field can be accessed by the name or index of its column, though
1345+
/// access by index is more efficient. Rows are 1-indexed.
1346+
///
1347+
/// Returns an `Error` value if the index does not reference a column or
1348+
/// the return type is not compatible with the Postgres type.
1349+
pub fn get<I: RowIndex, T: FromSql>(&self, idx: I)
1350+
-> Result<T, PostgresError> {
1351+
let idx = match idx.idx(self.stmt) {
1352+
Some(idx) => idx,
1353+
None => return Err(PgInvalidColumn)
1354+
};
1355+
FromSql::from_sql(&self.stmt.result_desc.get(idx).ty,
1356+
self.data.get(idx))
1357+
}
1358+
}
1359+
13531360
impl<'stmt> Container for PostgresRow<'stmt> {
13541361
#[inline]
13551362
fn len(&self) -> uint {
@@ -1358,47 +1365,74 @@ impl<'stmt> Container for PostgresRow<'stmt> {
13581365
}
13591366

13601367
impl<'stmt, I: RowIndex, T: FromSql> Index<I, T> for PostgresRow<'stmt> {
1368+
/// Retreives the contents of a field of the row.
1369+
///
1370+
/// A field can be accessed by the name or index of its column, though
1371+
/// access by index is more efficient. Rows are 1-indexed.
1372+
///
1373+
/// # Failure
1374+
///
1375+
/// Fails if the index does not reference a column or the return type is
1376+
/// not compatible with the Postgres type.
1377+
///
1378+
/// # Example
1379+
///
1380+
/// ```rust,no_run
1381+
/// # use postgres::{PostgresConnection, NoSsl};
1382+
/// # let conn = PostgresConnection::connect("", &NoSsl).unwrap();
1383+
/// # let stmt = conn.prepare("").unwrap();
1384+
/// # let mut result = stmt.query([]).unwrap();
1385+
/// # let row = result.next().unwrap();
1386+
/// let foo: i32 = row[1];
1387+
/// let bar: ~str = row["bar"];
1388+
/// ```
13611389
fn index(&self, idx: &I) -> T {
1362-
let idx = idx.idx(self.stmt);
1363-
FromSql::from_sql(&self.stmt.result_desc.get(idx).ty, self.data.get(idx)).unwrap()
1390+
match self.get(idx.clone()) {
1391+
Ok(ok) => ok,
1392+
Err(err) => fail!("error retrieving row: {}", err)
1393+
}
13641394
}
13651395
}
13661396

13671397
/// A trait implemented by types that can index into columns of a row.
1368-
pub trait RowIndex {
1369-
/// Returns the index of the appropriate column.
1370-
///
1371-
/// # Failure
1372-
///
1373-
/// Fails if there is no corresponding column.
1374-
fn idx(&self, stmt: &PostgresStatement) -> uint;
1398+
pub trait RowIndex: Clone {
1399+
/// Returns the index of the appropriate column, or `None` if no such
1400+
/// column exists.
1401+
fn idx(&self, stmt: &PostgresStatement) -> Option<uint>;
13751402
}
13761403

13771404
impl RowIndex for uint {
13781405
#[inline]
1379-
fn idx(&self, _stmt: &PostgresStatement) -> uint {
1380-
assert!(*self != 0, "out of bounds row access");
1381-
*self - 1
1406+
fn idx(&self, stmt: &PostgresStatement) -> Option<uint> {
1407+
let idx = *self - 1;
1408+
if idx >= stmt.result_desc.len() {
1409+
None
1410+
} else {
1411+
Some(idx)
1412+
}
13821413
}
13831414
}
13841415

13851416
// This is a convenience as the 1 in get[1] resolves to int :(
13861417
impl RowIndex for int {
13871418
#[inline]
1388-
fn idx(&self, _stmt: &PostgresStatement) -> uint {
1389-
assert!(*self >= 1, "out of bounds row access");
1390-
(*self - 1) as uint
1419+
fn idx(&self, stmt: &PostgresStatement) -> Option<uint> {
1420+
if *self < 0 {
1421+
return None;
1422+
}
1423+
1424+
(*self as uint).idx(stmt)
13911425
}
13921426
}
13931427

13941428
impl<'a> RowIndex for &'a str {
1395-
fn idx(&self, stmt: &PostgresStatement) -> uint {
1429+
fn idx(&self, stmt: &PostgresStatement) -> Option<uint> {
13961430
for (i, desc) in stmt.result_descriptions().iter().enumerate() {
13971431
if desc.name.as_slice() == *self {
1398-
return i;
1432+
return Some(i);
13991433
}
14001434
}
1401-
fail!("there is no column with name {}", *self);
1435+
None
14021436
}
14031437
}
14041438

src/test.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use error::{PgConnectDbError,
2222
PgWrongConnection,
2323
PgWrongParamCount,
2424
PgWrongType,
25+
PgInvalidColumn,
2526
DnsError,
2627
MissingPassword,
2728
Position,
@@ -739,7 +740,7 @@ fn test_too_many_params() {
739740
}
740741

741742
#[test]
742-
fn test_get_named() {
743+
fn test_index_named() {
743744
let conn = or_fail!(PostgresConnection::connect("postgres://postgres@localhost", &NoSsl));
744745
let stmt = or_fail!(conn.prepare("SELECT 10::INT as val"));
745746
let result = or_fail!(stmt.query([]));
@@ -749,14 +750,26 @@ fn test_get_named() {
749750

750751
#[test]
751752
#[should_fail]
752-
fn test_get_named_fail() {
753+
fn test_index_named_fail() {
753754
let conn = or_fail!(PostgresConnection::connect("postgres://postgres@localhost", &NoSsl));
754755
let stmt = or_fail!(conn.prepare("SELECT 10::INT as id"));
755756
let mut result = or_fail!(stmt.query([]));
756757

757758
let _: i32 = result.next().unwrap()["asdf"];
758759
}
759760

761+
#[test]
762+
fn test_get_named_err() {
763+
let conn = or_fail!(PostgresConnection::connect("postgres://postgres@localhost", &NoSsl));
764+
let stmt = or_fail!(conn.prepare("SELECT 10::INT as id"));
765+
let mut result = or_fail!(stmt.query([]));
766+
767+
match result.next().unwrap().get::<&str, i32>("asdf") {
768+
Err(PgInvalidColumn) => {}
769+
res => fail!("unexpected result {}", res),
770+
};
771+
}
772+
760773
#[test]
761774
fn test_custom_notice_handler() {
762775
static mut count: uint = 0;

0 commit comments

Comments
 (0)