Skip to content

Commit de06c71

Browse files
committed
Add FromSql::from_sql_nullable
Also clean up logic for type info queries to avoid panics on bogus responses.
1 parent 4942422 commit de06c71

File tree

3 files changed

+34
-35
lines changed

3 files changed

+34
-35
lines changed

src/lib.rs

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ const TYPEINFO_COMPOSITE_QUERY: &'static str = "__typeinfo_composite";
124124
/// A type alias of the result returned by many methods.
125125
pub type Result<T> = result::Result<T, Error>;
126126

127-
/// Trait for types that can handle Postgres notice messages
127+
/// A trait implemented by types that can handle Postgres notice messages.
128128
///
129129
/// It is implemented for all `Send + FnMut(DbError)` closures.
130130
pub trait HandleNotice: Send {
@@ -681,29 +681,23 @@ impl InnerConnection {
681681
try!(self.read_rows(&mut rows));
682682
let row = rows.pop_front().unwrap();
683683

684+
let get_raw = |i| row.get(i).and_then(|r| r.as_ref().map(|r| &**r));
685+
684686
let (name, type_, elem_oid, rngsubtype, basetype, schema, relid) = {
685687
let ctx = SessionInfo::new(&self.parameters);
686-
let name = try!(String::from_sql(&Type::Name, row[0].as_ref().unwrap(), &ctx)
688+
let name = try!(String::from_sql_nullable(&Type::Name, get_raw(0), &ctx)
687689
.map_err(Error::Conversion));
688-
let type_ = try!(i8::from_sql(&Type::Char, row[1].as_ref().unwrap(), &ctx)
690+
let type_ = try!(i8::from_sql_nullable(&Type::Char, get_raw(1), &ctx)
689691
.map_err(Error::Conversion));
690-
let elem_oid = try!(Oid::from_sql(&Type::Oid, row[2].as_ref().unwrap(), &ctx)
692+
let elem_oid = try!(Oid::from_sql_nullable(&Type::Oid, get_raw(2), &ctx)
691693
.map_err(Error::Conversion));
692-
let rngsubtype = match row[3] {
693-
Some(ref data) => {
694-
try!(Option::<Oid>::from_sql(&Type::Oid, data, &ctx)
695-
.map_err(Error::Conversion))
696-
}
697-
None => {
698-
try!(Option::<Oid>::from_sql_null(&Type::Oid, &ctx).map_err(Error::Conversion))
699-
}
700-
};
701-
let basetype = try!(Oid::from_sql(&Type::Oid, row[4].as_ref().unwrap(), &ctx)
694+
let rngsubtype = try!(Option::<Oid>::from_sql_nullable(&Type::Oid, get_raw(3), &ctx)
702695
.map_err(Error::Conversion));
703-
let schema =
704-
try!(String::from_sql(&Type::Name, row[5].as_ref().unwrap(), &ctx)
705-
.map_err(Error::Conversion));
706-
let relid = try!(Oid::from_sql(&Type::Oid, row[6].as_ref().unwrap(), &ctx)
696+
let basetype = try!(Oid::from_sql_nullable(&Type::Oid, get_raw(4), &ctx)
697+
.map_err(Error::Conversion));
698+
let schema = try!(String::from_sql_nullable(&Type::Name, get_raw(5), &ctx)
699+
.map_err(Error::Conversion));
700+
let relid = try!(Oid::from_sql_nullable(&Type::Oid, get_raw(6), &ctx)
707701
.map_err(Error::Conversion));
708702
(name, type_, elem_oid, rngsubtype, basetype, schema, relid)
709703
};
@@ -763,7 +757,8 @@ impl InnerConnection {
763757
let ctx = SessionInfo::new(&self.parameters);
764758
let mut variants = vec![];
765759
for row in rows {
766-
variants.push(try!(String::from_sql(&Type::Name, row[0].as_ref().unwrap(), &ctx)
760+
let raw = row.get(0).and_then(|r| r.as_ref().map(|r| &**r));
761+
variants.push(try!(String::from_sql_nullable(&Type::Name, raw, &ctx)
767762
.map_err(Error::Conversion)));
768763
}
769764

@@ -796,11 +791,11 @@ impl InnerConnection {
796791
let mut fields = vec![];
797792
for row in rows {
798793
let (name, type_) = {
794+
let get_raw = |i| row.get(i).and_then(|r| r.as_ref().map(|r| &**r));
799795
let ctx = SessionInfo::new(&self.parameters);
800-
let name =
801-
try!(String::from_sql(&Type::Name, row[0].as_ref().unwrap(), &ctx)
802-
.map_err(Error::Conversion));
803-
let type_ = try!(Oid::from_sql(&Type::Oid, row[1].as_ref().unwrap(), &ctx)
796+
let name = try!(String::from_sql_nullable(&Type::Name, get_raw(0), &ctx)
797+
.map_err(Error::Conversion));
798+
let type_ = try!(Oid::from_sql_nullable(&Type::Oid, get_raw(1), &ctx)
804799
.map_err(Error::Conversion));
805800
(name, type_)
806801
};

src/rows.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,9 @@ impl<'a> Row<'a> {
237237
return Some(Err(Error::Conversion(Box::new(WrongType::new(ty.clone())))));
238238
}
239239
let conn = self.stmt.conn().0.borrow();
240-
let value = match self.data[idx] {
241-
Some(ref data) => {
242-
FromSql::from_sql(ty, data, &SessionInfo::new(&conn.parameters))
243-
}
244-
None => FromSql::from_sql_null(ty, &SessionInfo::new(&conn.parameters)),
245-
};
240+
let value = FromSql::from_sql_nullable(ty,
241+
self.data[idx].as_ref().map(|r| &**r),
242+
&SessionInfo::new(&conn.parameters));
246243
Some(value.map_err(Error::Conversion))
247244
}
248245

src/types/mod.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,18 @@ pub trait FromSql: Sized {
325325
Err(Box::new(WasNull))
326326
}
327327

328+
/// A convenience function that delegates to `from_sql` and `from_sql_null` depending on the
329+
/// value of `raw`.
330+
fn from_sql_nullable(ty: &Type,
331+
raw: Option<&[u8]>,
332+
ctx: &SessionInfo)
333+
-> Result<Self, Box<Error + Sync + Send>> {
334+
match raw {
335+
Some(raw) => Self::from_sql(ty, raw, ctx),
336+
None => Self::from_sql_null(ty, ctx),
337+
}
338+
}
339+
328340
/// Determines if a value of this type can be created from the specified
329341
/// Postgres `Type`.
330342
fn accepts(ty: &Type) -> bool;
@@ -363,12 +375,7 @@ impl<T: FromSql> FromSql for Vec<T> {
363375
}
364376

365377
array.values()
366-
.and_then(|v| {
367-
match v {
368-
Some(v) => T::from_sql(member_type, v, info),
369-
None => T::from_sql_null(member_type, info),
370-
}
371-
})
378+
.and_then(|v| T::from_sql_nullable(member_type, v, info))
372379
.collect()
373380
}
374381

0 commit comments

Comments
 (0)