Skip to content

Commit 5f8a0b9

Browse files
committed
Overhaul FromSql
We can now have a blanket impl for Option and remove RawFromSql
1 parent ef0e15b commit 5f8a0b9

3 files changed

Lines changed: 136 additions & 100 deletions

File tree

src/lib.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -748,9 +748,12 @@ impl InnerConnection {
748748
let (name, elem_oid, rngsubtype): (String, Oid, Option<Oid>) =
749749
match try!(self.read_message()) {
750750
DataRow { row } => {
751-
(try!(FromSql::from_sql(&Type::Name, row[0].as_ref().map(|r| &**r))),
752-
try!(FromSql::from_sql(&Type::Oid, row[1].as_ref().map(|r| &**r))),
753-
try!(FromSql::from_sql(&Type::Oid, row[2].as_ref().map(|r| &**r))))
751+
(try!(FromSql::from_sql_nullable(&Type::Name,
752+
row[0].as_ref().map(|r| &**r).as_mut())),
753+
try!(FromSql::from_sql_nullable(&Type::Oid,
754+
row[1].as_ref().map(|r| &**r).as_mut())),
755+
try!(FromSql::from_sql_nullable(&Type::Oid,
756+
row[2].as_ref().map(|r| &**r).as_mut())))
754757
}
755758
ErrorResponse { fields } => {
756759
try!(self.wait_for_ready());
@@ -1642,7 +1645,8 @@ impl<'stmt> Row<'stmt> {
16421645
/// the return type is not compatible with the Postgres type.
16431646
pub fn get_opt<I, T>(&self, idx: I) -> Result<T> where I: RowIndex, T: FromSql {
16441647
let idx = try!(idx.idx(self.stmt).ok_or(Error::InvalidColumn));
1645-
FromSql::from_sql(&self.stmt.columns[idx].type_, self.data[idx].as_ref().map(|e| &**e))
1648+
FromSql::from_sql_nullable(&self.stmt.columns[idx].type_,
1649+
self.data[idx].as_ref().map(|e| &**e).as_mut())
16461650
}
16471651

16481652
/// Retrieves the contents of a field of the row.

src/types/json.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use serialize::json;
33
use {Result, Error};
44
use types::{RawFromSql, RawToSql, Type};
55

6-
impl RawFromSql for json::Json {
7-
fn raw_from_sql<R: Reader>(ty: &Type, raw: &mut R) -> Result<json::Json> {
6+
impl FromSql for json::Json {
7+
fn from_sql<R: Reader>(ty: &Type, raw: &mut R) -> Result<json::Json> {
88
if let Type::Jsonb = *ty {
99
// We only support version 1 of the jsonb binary format
1010
if try!(raw.read_u8()) != 1 {
@@ -13,9 +13,9 @@ impl RawFromSql for json::Json {
1313
}
1414
json::Json::from_reader(raw).map_err(|_| Error::BadResponse)
1515
}
16-
}
1716

18-
from_raw_from_impl!(Type::Json, Type::Jsonb; json::Json);
17+
accepts!(Type::Json, Type::Jsonb);
18+
}
1919

2020
impl RawToSql for json::Json {
2121
fn raw_to_sql<W: Writer>(&self, ty: &Type, raw: &mut W) -> Result<()> {

src/types/mod.rs

Lines changed: 124 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ use error::Error;
1010

1111
pub use ugh_privacy::Unknown;
1212

13+
macro_rules! accepts {
14+
($($expected:pat),+) => (
15+
fn accepts(ty: &::types::Type) -> bool {
16+
match *ty {
17+
$($expected)|+ => true,
18+
_ => false
19+
}
20+
}
21+
)
22+
}
23+
1324
macro_rules! check_types {
1425
($($expected:pat),+; $actual:ident) => (
1526
match $actual {
@@ -347,51 +358,105 @@ make_postgres_type! {
347358
INT8RANGEARRAYOID => Int8RangeArray: Kind::Array(Type::Int8Range)
348359
}
349360

350-
/// A trait for types that can be created from a Postgres value
351-
pub trait FromSql {
352-
/// Creates a new value of this type from a buffer of Postgres data.
361+
/// A trait for types that can be created from a Postgres value.
362+
pub trait FromSql: Sized {
363+
/// Creates a new value of this type from a `Reader` of Postgres data.
353364
///
354-
/// If the value was `NULL`, the buffer will be `None`.
355-
fn from_sql(ty: &Type, raw: Option<&[u8]>) -> Result<Self>;
356-
}
365+
/// If the value was `NULL`, the `Reader` will be `None`.
366+
///
367+
/// The caller of this method is responsible for ensuring that this type
368+
/// is compatible with the Postgres `Type`.
369+
///
370+
/// The default implementation calls `FromSql::from_sql` when `raw` is
371+
/// `Some` and returns `Err(Error::WasNull)` when `raw` is `None`. It does
372+
/// not typically need to be overridden.
373+
fn from_sql_nullable<R: Reader>(ty: &Type, raw: Option<&mut R>) -> Result<Self> {
374+
match raw {
375+
Some(raw) => FromSql::from_sql(ty, raw),
376+
None => Err(Error::WasNull),
377+
}
378+
}
357379

358-
/// A utility trait used by `FromSql` implementations
359-
pub trait RawFromSql {
360380
/// Creates a new value of this type from a `Reader` of Postgres data.
361381
///
362-
/// It is the caller's responsibility to ensure that Postgres data of this
363-
/// type can be turned in to this type.
364-
fn raw_from_sql<R: Reader>(ty: &Type, raw: &mut R) -> Result<Self>;
382+
/// The caller of this method is responsible for ensuring that this type
383+
/// is compatible with the Postgres `Type`.
384+
fn from_sql<R: Reader>(ty: &Type, raw: &mut R) -> Result<Self>;
385+
386+
/// Determines if a value of this type can be created from the specified
387+
/// Postgres `Type`.
388+
fn accepts(ty: &Type) -> bool;
389+
}
390+
391+
impl<T: FromSql> FromSql for Option<T> {
392+
fn from_sql_nullable<R: Reader>(ty: &Type, raw: Option<&mut R>) -> Result<Option<T>> {
393+
match raw {
394+
Some(raw) => <T as FromSql>::from_sql(ty, raw).map(|e| Some(e)),
395+
None => Ok(None),
396+
}
397+
}
398+
399+
fn from_sql<R: Reader>(ty: &Type, raw: &mut R) -> Result<Option<T>> {
400+
<T as FromSql>::from_sql(ty, raw).map(|e| Some(e))
401+
}
402+
403+
fn accepts(ty: &Type) -> bool {
404+
<T as FromSql>::accepts(ty)
405+
}
365406
}
366407

367-
impl RawFromSql for bool {
368-
fn raw_from_sql<R: Reader>(_: &Type, raw: &mut R) -> Result<bool> {
369-
Ok((try!(raw.read_u8())) != 0)
408+
impl FromSql for bool {
409+
fn from_sql<R: Reader>(_: &Type, raw: &mut R) -> Result<bool> {
410+
Ok(try!(raw.read_u8()) != 0)
370411
}
412+
413+
accepts!(Type::Bool);
371414
}
372415

373-
impl RawFromSql for Vec<u8> {
374-
fn raw_from_sql<R: Reader>(_: &Type, raw: &mut R) -> Result<Vec<u8>> {
416+
impl FromSql for Vec<u8> {
417+
fn from_sql<R: Reader>(_: &Type, raw: &mut R) -> Result<Vec<u8>> {
375418
Ok(try!(raw.read_to_end()))
376419
}
420+
421+
accepts!(Type::ByteA);
377422
}
378423

379-
impl RawFromSql for String {
380-
fn raw_from_sql<R: Reader>(_: &Type, raw: &mut R) -> Result<String> {
424+
impl FromSql for String {
425+
fn from_sql<R: Reader>(_: &Type, raw: &mut R) -> Result<String> {
381426
String::from_utf8(try!(raw.read_to_end())).map_err(|_| Error::BadResponse)
382427
}
428+
429+
fn accepts(ty: &Type) -> bool {
430+
match *ty {
431+
Type::Varchar | Type::Text | Type::CharN | Type::Name => true,
432+
Type::Unknown(ref u) if u.name() == "citext" => true,
433+
_ => false,
434+
}
435+
}
383436
}
384437

385-
raw_from_impl!(i8, read_i8);
386-
raw_from_impl!(i16, read_be_i16);
387-
raw_from_impl!(i32, read_be_i32);
388-
raw_from_impl!(u32, read_be_u32);
389-
raw_from_impl!(i64, read_be_i64);
390-
raw_from_impl!(f32, read_be_f32);
391-
raw_from_impl!(f64, read_be_f64);
438+
macro_rules! primitive_from {
439+
($t:ty, $f:ident, $($expected:pat),+) => {
440+
impl FromSql for $t {
441+
fn from_sql<R: Reader>(_: &Type, raw: &mut R) -> Result<$t> {
442+
Ok(try!(raw.$f()))
443+
}
392444

393-
impl RawFromSql for IpAddr {
394-
fn raw_from_sql<R: Reader>(_: &Type, raw: &mut R) -> Result<IpAddr> {
445+
accepts!($($expected),+);
446+
}
447+
}
448+
}
449+
450+
primitive_from!(i8, read_i8, Type::Char);
451+
primitive_from!(i16, read_be_i16, Type::Int2);
452+
primitive_from!(i32, read_be_i32, Type::Int4);
453+
primitive_from!(u32, read_be_u32, Type::Oid);
454+
primitive_from!(i64, read_be_i64, Type::Int8);
455+
primitive_from!(f32, read_be_f32, Type::Float4);
456+
primitive_from!(f64, read_be_f64, Type::Float8);
457+
458+
impl FromSql for IpAddr {
459+
fn from_sql<R: Reader>(_: &Type, raw: &mut R) -> Result<IpAddr> {
395460
let family = try!(raw.read_u8());
396461
let _bits = try!(raw.read_u8());
397462
let _is_cidr = try!(raw.read_u8());
@@ -416,83 +481,50 @@ impl RawFromSql for IpAddr {
416481
_ => Err(Error::BadResponse),
417482
}
418483
}
484+
485+
accepts!(Type::Inet, Type::Cidr);
419486
}
420487

421-
from_raw_from_impl!(Type::Bool; bool);
422-
from_raw_from_impl!(Type::ByteA; Vec<u8>);
423-
from_raw_from_impl!(Type::Char; i8);
424-
from_raw_from_impl!(Type::Int2; i16);
425-
from_raw_from_impl!(Type::Int4; i32);
426-
from_raw_from_impl!(Type::Oid; u32);
427-
from_raw_from_impl!(Type::Int8; i64);
428-
from_raw_from_impl!(Type::Float4; f32);
429-
from_raw_from_impl!(Type::Float8; f64);
430-
from_raw_from_impl!(Type::Inet, Type::Cidr; IpAddr);
488+
impl FromSql for HashMap<String, Option<String>> {
489+
fn from_sql<R: Reader>(_: &Type, raw: &mut R)
490+
-> Result<HashMap<String, Option<String>>> {
491+
let mut map = HashMap::new();
431492

432-
impl FromSql for Option<String> {
433-
fn from_sql(ty: &Type, raw: Option<&[u8]>) -> Result<Option<String>> {
434-
match *ty {
435-
Type::Varchar | Type::Text | Type::CharN | Type::Name => {}
436-
Type::Unknown(ref u) if u.name() == "citext" => {}
437-
_ => return Err(Error::WrongType(ty.clone()))
438-
}
493+
let count = try!(raw.read_be_i32());
439494

440-
match raw {
441-
Some(mut buf) => {
442-
Ok(Some(try!(RawFromSql::raw_from_sql(ty, &mut buf))))
443-
}
444-
None => Ok(None)
445-
}
446-
}
447-
}
495+
for _ in range(0, count) {
496+
let key_len = try!(raw.read_be_i32());
497+
let key = try!(raw.read_exact(key_len as usize));
498+
let key = match String::from_utf8(key) {
499+
Ok(key) => key,
500+
Err(_) => return Err(Error::BadResponse),
501+
};
448502

449-
from_option_impl!(String);
503+
let val_len = try!(raw.read_be_i32());
504+
let val = if val_len < 0 {
505+
None
506+
} else {
507+
let val = try!(raw.read_exact(val_len as usize));
508+
match String::from_utf8(val) {
509+
Ok(val) => Some(val),
510+
Err(_) => return Err(Error::BadResponse),
511+
}
512+
};
450513

451-
impl FromSql for Option<HashMap<String, Option<String>>> {
452-
fn from_sql(ty: &Type, raw: Option<&[u8]>)
453-
-> Result<Option<HashMap<String, Option<String>>>> {
454-
match *ty {
455-
Type::Unknown(ref u) if u.name() == "hstore" => {}
456-
_ => return Err(Error::WrongType(ty.clone()))
514+
map.insert(key, val);
457515
}
458516

459-
match raw {
460-
Some(buf) => {
461-
let mut rdr = buf;
462-
let mut map = HashMap::new();
463-
464-
let count = try!(rdr.read_be_i32());
465-
466-
for _ in range(0, count) {
467-
let key_len = try!(rdr.read_be_i32());
468-
let key = try!(rdr.read_exact(key_len as usize));
469-
let key = match String::from_utf8(key) {
470-
Ok(key) => key,
471-
Err(_) => return Err(Error::BadResponse),
472-
};
473-
474-
let val_len = try!(rdr.read_be_i32());
475-
let val = if val_len < 0 {
476-
None
477-
} else {
478-
let val = try!(rdr.read_exact(val_len as usize));
479-
match String::from_utf8(val) {
480-
Ok(val) => Some(val),
481-
Err(_) => return Err(Error::BadResponse),
482-
}
483-
};
484-
485-
map.insert(key, val);
486-
}
487-
Ok(Some(map))
488-
}
489-
None => Ok(None)
517+
Ok(map)
518+
}
519+
520+
fn accepts(ty: &Type) -> bool {
521+
match *ty {
522+
Type::Unknown(ref u) if u.name() == "hstore" => true,
523+
_ => false
490524
}
491525
}
492526
}
493527

494-
from_option_impl!(HashMap<String, Option<String>>);
495-
496528
/// A trait for types that can be converted into Postgres values
497529
pub trait ToSql {
498530
/// Converts the value of `self` into the binary format appropriate for the

0 commit comments

Comments
 (0)