Use type conversions from protocol crate

This also changes ToSql and FromSql to explicitly deal in byte buffers.
This commit is contained in:
Steven Fackler 2016-09-14 23:27:33 -07:00
parent ff853811cb
commit f5ec24de78
16 changed files with 233 additions and 431 deletions

View File

@ -52,3 +52,6 @@ uuid = { version = ">= 0.1, < 0.4", optional = true }
[dev-dependencies] [dev-dependencies]
url = "1.0" url = "1.0"
[replace]
"fallible-iterator:0.1.2" = { git = "https://github.com/sfackler/rust-fallible-iterator" }

View File

@ -6,12 +6,12 @@ use std::ascii::AsciiExt;
use std::path::Path; use std::path::Path;
mod sqlstate; mod sqlstate;
mod types; mod type_gen;
fn main() { fn main() {
let path = Path::new("../src"); let path = Path::new("../src");
sqlstate::build(path); sqlstate::build(path);
types::build(path); type_gen::build(path);
} }
fn snake_to_camel(s: &str) -> String { fn snake_to_camel(s: &str) -> String {

View File

@ -20,7 +20,7 @@ struct Type {
} }
pub fn build(path: &Path) { pub fn build(path: &Path) {
let mut file = BufWriter::new(File::create(path.join("types/types.rs")).unwrap()); let mut file = BufWriter::new(File::create(path.join("types/type_gen.rs")).unwrap());
let ranges = parse_ranges(); let ranges = parse_ranges();
let types = parse_types(&ranges); let types = parse_types(&ranges);

View File

@ -528,7 +528,7 @@ impl InnerConnection {
let mut values = vec![]; let mut values = vec![];
for (param, ty) in params.iter().zip(param_types) { for (param, ty) in params.iter().zip(param_types) {
let mut buf = vec![]; let mut buf = vec![];
match try!(param.to_sql_checked(ty, &mut buf, &SessionInfo::new(self))) { match try!(param.to_sql_checked(ty, &mut buf, &SessionInfo::new(self)).map_err(Error::Conversion)) {
IsNull::Yes => values.push(None), IsNull::Yes => values.push(None),
IsNull::No => values.push(Some(buf)), IsNull::No => values.push(Some(buf)),
} }
@ -671,18 +671,30 @@ impl InnerConnection {
let (name, type_, elem_oid, rngsubtype, basetype, schema, relid) = { let (name, type_, elem_oid, rngsubtype, basetype, schema, relid) = {
let ctx = SessionInfo::new(self); let ctx = SessionInfo::new(self);
let name = try!(String::from_sql(&Type::Name, &mut &**row[0].as_ref().unwrap(), &ctx)); let name = try!(String::from_sql(&Type::Name, &mut &**row[0].as_ref().unwrap(), &ctx)
let type_ = try!(i8::from_sql(&Type::Char, &mut &**row[1].as_ref().unwrap(), &ctx)); .map_err(Error::Conversion));
let elem_oid = try!(Oid::from_sql(&Type::Oid, &mut &**row[2].as_ref().unwrap(), &ctx)); let type_ = try!(i8::from_sql(&Type::Char, &mut &**row[1].as_ref().unwrap(), &ctx)
.map_err(Error::Conversion));
let elem_oid = try!(Oid::from_sql(&Type::Oid, &mut &**row[2].as_ref().unwrap(), &ctx)
.map_err(Error::Conversion));
let rngsubtype = match row[3] { let rngsubtype = match row[3] {
Some(ref data) => try!(Option::<Oid>::from_sql(&Type::Oid, &mut &**data, &ctx)), Some(ref data) => {
None => try!(Option::<Oid>::from_sql_null(&Type::Oid, &ctx)), try!(Option::<Oid>::from_sql(&Type::Oid, &mut &**data, &ctx)
.map_err(Error::Conversion))
},
None => {
try!(Option::<Oid>::from_sql_null(&Type::Oid, &ctx)
.map_err(Error::Conversion))
},
}; };
let basetype = try!(Oid::from_sql(&Type::Oid, &mut &**row[4].as_ref().unwrap(), &ctx)); let basetype = try!(Oid::from_sql(&Type::Oid, &mut &**row[4].as_ref().unwrap(), &ctx)
.map_err(Error::Conversion));
let schema = try!(String::from_sql(&Type::Name, let schema = try!(String::from_sql(&Type::Name,
&mut &**row[5].as_ref().unwrap(), &mut &**row[5].as_ref().unwrap(),
&ctx)); &ctx)
let relid = try!(Oid::from_sql(&Type::Oid, &mut &**row[6].as_ref().unwrap(), &ctx)); .map_err(Error::Conversion));
let relid = try!(Oid::from_sql(&Type::Oid, &mut &**row[6].as_ref().unwrap(), &ctx)
.map_err(Error::Conversion));
(name, type_, elem_oid, rngsubtype, basetype, schema, relid) (name, type_, elem_oid, rngsubtype, basetype, schema, relid)
}; };
@ -743,7 +755,8 @@ impl InnerConnection {
for row in rows { for row in rows {
variants.push(try!(String::from_sql(&Type::Name, variants.push(try!(String::from_sql(&Type::Name,
&mut &**row[0].as_ref().unwrap(), &mut &**row[0].as_ref().unwrap(),
&ctx))); &ctx)
.map_err(Error::Conversion)));
} }
Ok(variants) Ok(variants)
@ -778,8 +791,10 @@ impl InnerConnection {
let ctx = SessionInfo::new(self); let ctx = SessionInfo::new(self);
let name = try!(String::from_sql(&Type::Name, let name = try!(String::from_sql(&Type::Name,
&mut &**row[0].as_ref().unwrap(), &mut &**row[0].as_ref().unwrap(),
&ctx)); &ctx)
let type_ = try!(Oid::from_sql(&Type::Oid, &mut &**row[1].as_ref().unwrap(), &ctx)); .map_err(Error::Conversion));
let type_ = try!(Oid::from_sql(&Type::Oid, &mut &**row[1].as_ref().unwrap(), &ctx)
.map_err(Error::Conversion));
(name, type_) (name, type_)
}; };
let type_ = try!(self.get_type(type_)); let type_ = try!(self.get_type(type_));

View File

@ -240,7 +240,7 @@ impl<'a> Row<'a> {
Some(ref data) => FromSql::from_sql(ty, &mut &**data, &SessionInfo::new(&*conn)), Some(ref data) => FromSql::from_sql(ty, &mut &**data, &SessionInfo::new(&*conn)),
None => FromSql::from_sql_null(ty, &SessionInfo::new(&*conn)), None => FromSql::from_sql_null(ty, &SessionInfo::new(&*conn)),
}; };
Some(value) Some(value.map_err(Error::Conversion))
} }
/// Retrieves the specified field as a raw buffer of Postgres data. /// Retrieves the specified field as a raw buffer of Postgres data.

View File

@ -1,20 +1,16 @@
extern crate bit_vec; extern crate bit_vec;
use std::io::prelude::*; use postgres_protocol::types;
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
use self::bit_vec::BitVec; use self::bit_vec::BitVec;
use std::error::Error;
use Result; use types::{FromSql, ToSql, IsNull, Type, SessionInfo};
use types::{FromSql, ToSql, IsNull, Type, SessionInfo, downcast};
impl FromSql for BitVec { impl FromSql for BitVec {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<BitVec> { fn from_sql(_: &Type, raw: &[u8], _: &SessionInfo) -> Result<BitVec, Box<Error + Sync + Send>> {
let len = try!(raw.read_i32::<BigEndian>()) as usize; let varbit = try!(types::varbit_from_sql(raw));
let mut bytes = vec![]; let mut bitvec = BitVec::from_bytes(varbit.bytes());
try!(raw.take(((len + 7) / 8) as u64).read_to_end(&mut bytes)); while bitvec.len() > varbit.len() {
let mut bitvec = BitVec::from_bytes(&bytes);
while bitvec.len() > len {
bitvec.pop(); bitvec.pop();
} }
@ -25,14 +21,8 @@ impl FromSql for BitVec {
} }
impl ToSql for BitVec { impl ToSql for BitVec {
fn to_sql<W: Write + ?Sized>(&self, fn to_sql(&self, _: &Type, mut out: &mut Vec<u8>, _: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
_: &Type, try!(types::varbit_to_sql(self.len(), self.to_bytes().into_iter(), out));
mut out: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
try!(out.write_i32::<BigEndian>(try!(downcast(self.len()))));
try!(out.write_all(&self.to_bytes()));
Ok(IsNull::No) Ok(IsNull::No)
} }

View File

@ -1,12 +1,10 @@
extern crate chrono; extern crate chrono;
use std::io::prelude::*; use postgres_protocol::types;
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
use self::chrono::{Duration, NaiveDate, NaiveTime, NaiveDateTime, DateTime, UTC, Local, use self::chrono::{Duration, NaiveDate, NaiveTime, NaiveDateTime, DateTime, UTC, Local,
FixedOffset}; FixedOffset};
use std::error::Error;
use Result;
use error::Error;
use types::{FromSql, ToSql, IsNull, Type, SessionInfo}; use types::{FromSql, ToSql, IsNull, Type, SessionInfo};
fn base() -> NaiveDateTime { fn base() -> NaiveDateTime {
@ -14,8 +12,8 @@ fn base() -> NaiveDateTime {
} }
impl FromSql for NaiveDateTime { impl FromSql for NaiveDateTime {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<NaiveDateTime> { fn from_sql(_: &Type, raw: &[u8], _: &SessionInfo) -> Result<NaiveDateTime, Box<Error + Sync + Send>> {
let t = try!(raw.read_i64::<BigEndian>()); let t = try!(types::timestamp_from_sql(raw));
Ok(base() + Duration::microseconds(t)) Ok(base() + Duration::microseconds(t))
} }
@ -23,16 +21,12 @@ impl FromSql for NaiveDateTime {
} }
impl ToSql for NaiveDateTime { impl ToSql for NaiveDateTime {
fn to_sql<W: Write + ?Sized>(&self, fn to_sql(&self, _: &Type, w: &mut Vec<u8>, _: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
_: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
let time = match (*self - base()).num_microseconds() { let time = match (*self - base()).num_microseconds() {
Some(time) => time, Some(time) => time,
None => return Err(Error::Conversion("value too large to transmit".into())), None => return Err("value too large to transmit".into()),
}; };
try!(w.write_i64::<BigEndian>(time)); types::timestamp_to_sql(time, w);
Ok(IsNull::No) Ok(IsNull::No)
} }
@ -41,7 +35,7 @@ impl ToSql for NaiveDateTime {
} }
impl FromSql for DateTime<UTC> { impl FromSql for DateTime<UTC> {
fn from_sql<R: Read>(type_: &Type, raw: &mut R, info: &SessionInfo) -> Result<DateTime<UTC>> { fn from_sql(type_: &Type, raw: &[u8], info: &SessionInfo) -> Result<DateTime<UTC>, Box<Error + Sync + Send>> {
let naive = try!(NaiveDateTime::from_sql(type_, raw, info)); let naive = try!(NaiveDateTime::from_sql(type_, raw, info));
Ok(DateTime::from_utc(naive, UTC)) Ok(DateTime::from_utc(naive, UTC))
} }
@ -50,11 +44,7 @@ impl FromSql for DateTime<UTC> {
} }
impl ToSql for DateTime<UTC> { impl ToSql for DateTime<UTC> {
fn to_sql<W: Write + ?Sized>(&self, fn to_sql(&self, type_: &Type, w: &mut Vec<u8>, info: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
type_: &Type,
mut w: &mut W,
info: &SessionInfo)
-> Result<IsNull> {
self.naive_utc().to_sql(type_, w, info) self.naive_utc().to_sql(type_, w, info)
} }
@ -63,7 +53,7 @@ impl ToSql for DateTime<UTC> {
} }
impl FromSql for DateTime<Local> { impl FromSql for DateTime<Local> {
fn from_sql<R: Read>(type_: &Type, raw: &mut R, info: &SessionInfo) -> Result<DateTime<Local>> { fn from_sql(type_: &Type, raw: &[u8], info: &SessionInfo) -> Result<DateTime<Local>, Box<Error + Sync + Send>> {
let utc = try!(DateTime::<UTC>::from_sql(type_, raw, info)); let utc = try!(DateTime::<UTC>::from_sql(type_, raw, info));
Ok(utc.with_timezone(&Local)) Ok(utc.with_timezone(&Local))
} }
@ -72,11 +62,7 @@ impl FromSql for DateTime<Local> {
} }
impl ToSql for DateTime<Local> { impl ToSql for DateTime<Local> {
fn to_sql<W: Write + ?Sized>(&self, fn to_sql(&self, type_: &Type, mut w: &mut Vec<u8>, info: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
type_: &Type,
mut w: &mut W,
info: &SessionInfo)
-> Result<IsNull> {
self.with_timezone(&UTC).to_sql(type_, w, info) self.with_timezone(&UTC).to_sql(type_, w, info)
} }
@ -85,10 +71,7 @@ impl ToSql for DateTime<Local> {
} }
impl FromSql for DateTime<FixedOffset> { impl FromSql for DateTime<FixedOffset> {
fn from_sql<R: Read>(type_: &Type, fn from_sql(type_: &Type, raw: &[u8], info: &SessionInfo) -> Result<DateTime<FixedOffset>, Box<Error + Sync + Send>> {
raw: &mut R,
info: &SessionInfo)
-> Result<DateTime<FixedOffset>> {
let utc = try!(DateTime::<UTC>::from_sql(type_, raw, info)); let utc = try!(DateTime::<UTC>::from_sql(type_, raw, info));
Ok(utc.with_timezone(&FixedOffset::east(0))) Ok(utc.with_timezone(&FixedOffset::east(0)))
} }
@ -97,11 +80,7 @@ impl FromSql for DateTime<FixedOffset> {
} }
impl ToSql for DateTime<FixedOffset> { impl ToSql for DateTime<FixedOffset> {
fn to_sql<W: Write + ?Sized>(&self, fn to_sql(&self, type_: &Type, w: &mut Vec<u8>, info: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
type_: &Type,
mut w: &mut W,
info: &SessionInfo)
-> Result<IsNull> {
self.with_timezone(&UTC).to_sql(type_, w, info) self.with_timezone(&UTC).to_sql(type_, w, info)
} }
@ -110,8 +89,8 @@ impl ToSql for DateTime<FixedOffset> {
} }
impl FromSql for NaiveDate { impl FromSql for NaiveDate {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<NaiveDate> { fn from_sql(_: &Type, raw: &[u8], _: &SessionInfo) -> Result<NaiveDate, Box<Error + Sync + Send>> {
let jd = try!(raw.read_i32::<BigEndian>()); let jd = try!(types::date_from_sql(raw));
Ok(base().date() + Duration::days(jd as i64)) Ok(base().date() + Duration::days(jd as i64))
} }
@ -119,17 +98,13 @@ impl FromSql for NaiveDate {
} }
impl ToSql for NaiveDate { impl ToSql for NaiveDate {
fn to_sql<W: Write + ?Sized>(&self, fn to_sql(&self, _: &Type, w: &mut Vec<u8>, _: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
_: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
let jd = (*self - base().date()).num_days(); let jd = (*self - base().date()).num_days();
if jd > i32::max_value() as i64 || jd < i32::min_value() as i64 { if jd > i32::max_value() as i64 || jd < i32::min_value() as i64 {
return Err(Error::Conversion("value too large to transmit".into())); return Err("value too large to transmit".into());
} }
try!(w.write_i32::<BigEndian>(jd as i32)); types::date_to_sql(jd as i32, w);
Ok(IsNull::No) Ok(IsNull::No)
} }
@ -138,8 +113,8 @@ impl ToSql for NaiveDate {
} }
impl FromSql for NaiveTime { impl FromSql for NaiveTime {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<NaiveTime> { fn from_sql(_: &Type, raw: &[u8], _: &SessionInfo) -> Result<NaiveTime, Box<Error + Sync + Send>> {
let usec = try!(raw.read_i64::<BigEndian>()); let usec = try!(types::time_from_sql(raw));
Ok(NaiveTime::from_hms(0, 0, 0) + Duration::microseconds(usec)) Ok(NaiveTime::from_hms(0, 0, 0) + Duration::microseconds(usec))
} }
@ -147,17 +122,13 @@ impl FromSql for NaiveTime {
} }
impl ToSql for NaiveTime { impl ToSql for NaiveTime {
fn to_sql<W: Write + ?Sized>(&self, fn to_sql(&self, _: &Type, w: &mut Vec<u8>, _: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
_: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
let delta = *self - NaiveTime::from_hms(0, 0, 0); let delta = *self - NaiveTime::from_hms(0, 0, 0);
let time = match delta.num_microseconds() { let time = match delta.num_microseconds() {
Some(time) => time, Some(time) => time,
None => return Err(Error::Conversion("value too large to transmit".into())), None => return Err("value too large to transmit".into()),
}; };
try!(w.write_i64::<BigEndian>(time)); types::time_to_sql(time, w);
Ok(IsNull::No) Ok(IsNull::No)
} }

View File

@ -1,16 +1,14 @@
extern crate eui48; extern crate eui48;
use std::io::prelude::*;
use self::eui48::MacAddress; use self::eui48::MacAddress;
use std::error::Error;
use postgres_protocol::types;
use types::{FromSql, ToSql, Type, IsNull, SessionInfo}; use types::{FromSql, ToSql, Type, IsNull, SessionInfo};
use Result;
impl FromSql for MacAddress { impl FromSql for MacAddress {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<MacAddress> { fn from_sql(_: &Type, raw: &[u8], _: &SessionInfo) -> Result<MacAddress, Box<Error + Sync + Send>> {
let mut bytes = [0; 6]; let bytes = try!(types::macaddr_from_sql(raw));
try!(raw.read_exact(&mut bytes));
Ok(MacAddress::new(bytes)) Ok(MacAddress::new(bytes))
} }
@ -18,8 +16,10 @@ impl FromSql for MacAddress {
} }
impl ToSql for MacAddress { impl ToSql for MacAddress {
fn to_sql<W: Write + ?Sized>(&self, _: &Type, w: &mut W, _: &SessionInfo) -> Result<IsNull> { fn to_sql(&self, _: &Type, w: &mut Vec<u8>, _: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
try!(w.write_all(self.as_bytes())); let mut bytes = [0; 6];
bytes.copy_from_slice(self.as_bytes());
types::macaddr_to_sql(bytes, w);
Ok(IsNull::No) Ok(IsNull::No)
} }

View File

@ -1,19 +1,18 @@
//! Traits dealing with Postgres data types //! Traits dealing with Postgres data types
use fallible_iterator::FallibleIterator;
use postgres_protocol::types::{self, ArrayDimension};
use std::collections::HashMap; use std::collections::HashMap;
use std::error; use std::error::Error;
use std::fmt; use std::fmt;
use std::io::prelude::*;
use std::sync::Arc; use std::sync::Arc;
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
#[doc(inline)] #[doc(inline)]
pub use postgres_protocol::message::Oid; pub use postgres_protocol::Oid;
pub use self::types::Type; pub use self::type_gen::Type;
pub use self::special::{Date, Timestamp}; pub use self::special::{Date, Timestamp};
use {Result, SessionInfoNew, InnerConnection, OtherNew, WrongTypeNew, FieldNew}; use {SessionInfoNew, InnerConnection, OtherNew, WrongTypeNew, FieldNew};
use error::Error;
/// Generates a simple implementation of `ToSql::accepts` which accepts the /// Generates a simple implementation of `ToSql::accepts` which accepts the
/// types passed to it. /// types passed to it.
@ -37,9 +36,9 @@ macro_rules! to_sql_checked {
() => { () => {
fn to_sql_checked(&self, fn to_sql_checked(&self,
ty: &$crate::types::Type, ty: &$crate::types::Type,
out: &mut ::std::io::Write, out: &mut ::std::vec::Vec<u8>,
ctx: &$crate::types::SessionInfo) ctx: &$crate::types::SessionInfo)
-> $crate::Result<$crate::types::IsNull> { -> ::std::result::Result<$crate::types::IsNull, Box<::std::error::Error + ::std::marker::Sync + ::std::marker::Send>> {
$crate::types::__to_sql_checked(self, ty, out, ctx) $crate::types::__to_sql_checked(self, ty, out, ctx)
} }
} }
@ -48,11 +47,11 @@ macro_rules! to_sql_checked {
// WARNING: this function is not considered part of this crate's public API. // WARNING: this function is not considered part of this crate's public API.
// It is subject to change at any time. // It is subject to change at any time.
#[doc(hidden)] #[doc(hidden)]
pub fn __to_sql_checked<T>(v: &T, ty: &Type, out: &mut Write, ctx: &SessionInfo) -> Result<IsNull> pub fn __to_sql_checked<T>(v: &T, ty: &Type, out: &mut Vec<u8>, ctx: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>>
where T: ToSql where T: ToSql
{ {
if !T::accepts(ty) { if !T::accepts(ty) {
return Err(Error::Conversion(Box::new(WrongType(ty.clone())))); return Err(Box::new(WrongType(ty.clone())));
} }
v.to_sql(ty, out, ctx) v.to_sql(ty, out, ctx)
} }
@ -73,7 +72,7 @@ mod chrono;
mod eui48; mod eui48;
mod special; mod special;
mod types; mod type_gen;
/// A structure providing information for conversion methods. /// A structure providing information for conversion methods.
pub struct SessionInfo<'a> { pub struct SessionInfo<'a> {
@ -214,11 +213,11 @@ pub struct WasNull;
impl fmt::Display for WasNull { impl fmt::Display for WasNull {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(error::Error::description(self)) fmt.write_str(self.description())
} }
} }
impl error::Error for WasNull { impl Error for WasNull {
fn description(&self) -> &str { fn description(&self) -> &str {
"a Postgres value was `NULL`" "a Postgres value was `NULL`"
} }
@ -237,7 +236,7 @@ impl fmt::Display for WrongType {
} }
} }
impl error::Error for WrongType { impl Error for WrongType {
fn description(&self) -> &str { fn description(&self) -> &str {
"cannot convert to or from a Postgres value" "cannot convert to or from a Postgres value"
} }
@ -303,12 +302,12 @@ impl WrongTypeNew for WrongType {
/// `FromSql` is implemented for `Vec<T>` where `T` implements `FromSql`, and /// `FromSql` is implemented for `Vec<T>` where `T` implements `FromSql`, and
/// corresponds to one-dimensional Postgres arrays. /// corresponds to one-dimensional Postgres arrays.
pub trait FromSql: Sized { pub trait FromSql: Sized {
/// Creates a new value of this type from a `Read`er of the binary format /// Creates a new value of this type from a buffer of data of the specified
/// of the specified Postgres `Type`. /// Postgres `Type` in its binary format.
/// ///
/// The caller of this method is responsible for ensuring that this type /// The caller of this method is responsible for ensuring that this type
/// is compatible with the Postgres `Type`. /// is compatible with the Postgres `Type`.
fn from_sql<R: Read>(ty: &Type, raw: &mut R, ctx: &SessionInfo) -> Result<Self>; fn from_sql(ty: &Type, raw: &[u8], ctx: &SessionInfo) -> Result<Self, Box<Error + Sync + Send>>;
/// Creates a new value of this type from a `NULL` SQL value. /// Creates a new value of this type from a `NULL` SQL value.
/// ///
@ -316,10 +315,10 @@ pub trait FromSql: Sized {
/// is compatible with the Postgres `Type`. /// is compatible with the Postgres `Type`.
/// ///
/// The default implementation returns /// The default implementation returns
/// `Err(Error::Conversion(Box::new(WasNull))`. /// `Err(Box::new(WasNull))`.
#[allow(unused_variables)] #[allow(unused_variables)]
fn from_sql_null(ty: &Type, ctx: &SessionInfo) -> Result<Self> { fn from_sql_null(ty: &Type, ctx: &SessionInfo) -> Result<Self, Box<Error + Sync + Send>> {
Err(Error::Conversion(Box::new(WasNull))) Err(Box::new(WasNull))
} }
/// Determines if a value of this type can be created from the specified /// Determines if a value of this type can be created from the specified
@ -328,11 +327,11 @@ pub trait FromSql: Sized {
} }
impl<T: FromSql> FromSql for Option<T> { impl<T: FromSql> FromSql for Option<T> {
fn from_sql<R: Read>(ty: &Type, raw: &mut R, ctx: &SessionInfo) -> Result<Option<T>> { fn from_sql(ty: &Type, raw: &[u8], ctx: &SessionInfo) -> Result<Option<T>, Box<Error + Sync + Send>> {
<T as FromSql>::from_sql(ty, raw, ctx).map(Some) <T as FromSql>::from_sql(ty, raw, ctx).map(Some)
} }
fn from_sql_null(_: &Type, _: &SessionInfo) -> Result<Option<T>> { fn from_sql_null(_: &Type, _: &SessionInfo) -> Result<Option<T>, Box<Error + Sync + Send>> {
Ok(None) Ok(None)
} }
@ -341,49 +340,26 @@ impl<T: FromSql> FromSql for Option<T> {
} }
} }
impl FromSql for bool {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<bool> {
Ok(try!(raw.read_u8()) != 0)
}
accepts!(Type::Bool);
}
impl<T: FromSql> FromSql for Vec<T> { impl<T: FromSql> FromSql for Vec<T> {
fn from_sql<R: Read>(ty: &Type, raw: &mut R, info: &SessionInfo) -> Result<Vec<T>> { fn from_sql(ty: &Type, raw: &[u8], info: &SessionInfo) -> Result<Vec<T>, Box<Error + Sync + Send>> {
let member_type = match *ty.kind() { let member_type = match *ty.kind() {
Kind::Array(ref member) => member, Kind::Array(ref member) => member,
_ => panic!("expected array type"), _ => panic!("expected array type"),
}; };
let dimensions = try!(raw.read_i32::<BigEndian>()); let array = try!(types::array_from_sql(raw));
if dimensions > 1 { if try!(array.dimensions().count()) > 1 {
return Err(Error::Conversion("array contains too many dimensions".into())); return Err("array contains too many dimensions".into());
} }
let _has_nulls = try!(raw.read_i32::<BigEndian>()); array.values()
let _member_oid = try!(raw.read_u32::<BigEndian>()); .and_then(|v| {
match v {
if dimensions == 0 { Some(v) => T::from_sql(&member_type, v, info),
return Ok(vec![]); None => T::from_sql_null(&member_type, info),
} }
})
let count = try!(raw.read_i32::<BigEndian>()); .collect()
let _index_offset = try!(raw.read_i32::<BigEndian>());
let mut out = Vec::with_capacity(count as usize);
for _ in 0..count {
let len = try!(raw.read_i32::<BigEndian>());
let value = if len < 0 {
try!(T::from_sql_null(&member_type, info))
} else {
let mut raw = raw.take(len as u64);
try!(T::from_sql(&member_type, &mut raw, info))
};
out.push(value)
}
Ok(out)
} }
fn accepts(ty: &Type) -> bool { fn accepts(ty: &Type) -> bool {
@ -395,20 +371,16 @@ impl<T: FromSql> FromSql for Vec<T> {
} }
impl FromSql for Vec<u8> { impl FromSql for Vec<u8> {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<Vec<u8>> { fn from_sql(_: &Type, raw: &[u8], _: &SessionInfo) -> Result<Vec<u8>, Box<Error + Sync + Send>> {
let mut buf = vec![]; Ok(types::bytea_from_sql(raw).to_owned())
try!(raw.read_to_end(&mut buf));
Ok(buf)
} }
accepts!(Type::Bytea); accepts!(Type::Bytea);
} }
impl FromSql for String { impl FromSql for String {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<String> { fn from_sql(_: &Type, raw: &[u8], _: &SessionInfo) -> Result<String, Box<Error + Sync + Send>> {
let mut buf = vec![]; types::text_from_sql(raw).map(|b| b.to_owned())
try!(raw.read_to_end(&mut buf));
String::from_utf8(buf).map_err(|err| Error::Conversion(Box::new(err)))
} }
fn accepts(ty: &Type) -> bool { fn accepts(ty: &Type) -> bool {
@ -420,19 +392,11 @@ impl FromSql for String {
} }
} }
impl FromSql for i8 { macro_rules! simple_from {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<i8> {
Ok(try!(raw.read_i8()))
}
accepts!(Type::Char);
}
macro_rules! primitive_from {
($t:ty, $f:ident, $($expected:pat),+) => { ($t:ty, $f:ident, $($expected:pat),+) => {
impl FromSql for $t { impl FromSql for $t {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<$t> { fn from_sql(_: &Type, raw: &[u8], _: &SessionInfo) -> Result<$t, Box<Error + Sync + Send>> {
Ok(try!(raw.$f::<BigEndian>())) types::$f(raw)
} }
accepts!($($expected),+); accepts!($($expected),+);
@ -440,47 +404,20 @@ macro_rules! primitive_from {
} }
} }
primitive_from!(i16, read_i16, Type::Int2); simple_from!(bool, bool_from_sql, Type::Bool);
primitive_from!(i32, read_i32, Type::Int4); simple_from!(i8, char_from_sql, Type::Char);
primitive_from!(u32, read_u32, Type::Oid); simple_from!(i16, int2_from_sql, Type::Int2);
primitive_from!(i64, read_i64, Type::Int8); simple_from!(i32, int4_from_sql, Type::Int4);
primitive_from!(f32, read_f32, Type::Float4); simple_from!(u32, oid_from_sql, Type::Oid);
primitive_from!(f64, read_f64, Type::Float8); simple_from!(i64, int8_from_sql, Type::Int8);
simple_from!(f32, float4_from_sql, Type::Float4);
simple_from!(f64, float8_from_sql, Type::Float8);
impl FromSql for HashMap<String, Option<String>> { impl FromSql for HashMap<String, Option<String>> {
fn from_sql<R: Read>(_: &Type, fn from_sql(_: &Type, raw: &[u8], _: &SessionInfo) -> Result<HashMap<String, Option<String>>, Box<Error + Sync + Send>> {
raw: &mut R, try!(types::hstore_from_sql(raw))
_: &SessionInfo) .map(|(k, v)| (k.to_owned(), v.map(|v| v.to_owned())))
-> Result<HashMap<String, Option<String>>> { .collect()
let mut map = HashMap::new();
let count = try!(raw.read_i32::<BigEndian>());
for _ in 0..count {
let key_len = try!(raw.read_i32::<BigEndian>());
let mut key = vec![0; key_len as usize];
try!(raw.read_exact(&mut key));
let key = match String::from_utf8(key) {
Ok(key) => key,
Err(err) => return Err(Error::Conversion(Box::new(err))),
};
let val_len = try!(raw.read_i32::<BigEndian>());
let val = if val_len < 0 {
None
} else {
let mut val = vec![0; val_len as usize];
try!(raw.read_exact(&mut val));
match String::from_utf8(val) {
Ok(val) => Some(val),
Err(err) => return Err(Error::Conversion(Box::new(err))),
}
};
map.insert(key, val);
}
Ok(map)
} }
fn accepts(ty: &Type) -> bool { fn accepts(ty: &Type) -> bool {
@ -556,7 +493,7 @@ pub enum IsNull {
/// 1. /// 1.
pub trait ToSql: fmt::Debug { pub trait ToSql: fmt::Debug {
/// Converts the value of `self` into the binary format of the specified /// Converts the value of `self` into the binary format of the specified
/// Postgres `Type`, writing it to `out`. /// Postgres `Type`, appending it to `out`.
/// ///
/// The caller of this method is responsible for ensuring that this type /// The caller of this method is responsible for ensuring that this type
/// is compatible with the Postgres `Type`. /// is compatible with the Postgres `Type`.
@ -564,9 +501,7 @@ pub trait ToSql: fmt::Debug {
/// The return value indicates if this value should be represented as /// The return value indicates if this value should be represented as
/// `NULL`. If this is the case, implementations **must not** write /// `NULL`. If this is the case, implementations **must not** write
/// anything to `out`. /// anything to `out`.
fn to_sql<W: ?Sized>(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) -> Result<IsNull> fn to_sql(&self, ty: &Type, out: &mut Vec<u8>, ctx: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> where Self: Sized;
where Self: Sized,
W: Write;
/// Determines if a value of this type can be converted to the specified /// Determines if a value of this type can be converted to the specified
/// Postgres `Type`. /// Postgres `Type`.
@ -576,35 +511,25 @@ pub trait ToSql: fmt::Debug {
/// ///
/// *All* implementations of this method should be generated by the /// *All* implementations of this method should be generated by the
/// `to_sql_checked!()` macro. /// `to_sql_checked!()` macro.
fn to_sql_checked(&self, ty: &Type, out: &mut Write, ctx: &SessionInfo) -> Result<IsNull>; fn to_sql_checked(&self, ty: &Type, out: &mut Vec<u8>, ctx: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>>;
} }
impl<'a, T> ToSql for &'a T impl<'a, T> ToSql for &'a T
where T: ToSql where T: ToSql
{ {
to_sql_checked!(); fn to_sql(&self, ty: &Type, out: &mut Vec<u8>, ctx: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql<W: Write + ?Sized>(&self,
ty: &Type,
out: &mut W,
ctx: &SessionInfo)
-> Result<IsNull> {
(*self).to_sql(ty, out, ctx) (*self).to_sql(ty, out, ctx)
} }
fn accepts(ty: &Type) -> bool { fn accepts(ty: &Type) -> bool {
T::accepts(ty) T::accepts(ty)
} }
to_sql_checked!();
} }
impl<T: ToSql> ToSql for Option<T> { impl<T: ToSql> ToSql for Option<T> {
to_sql_checked!(); fn to_sql(&self, ty: &Type, out: &mut Vec<u8>, ctx: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql<W: Write + ?Sized>(&self,
ty: &Type,
out: &mut W,
ctx: &SessionInfo)
-> Result<IsNull> {
match *self { match *self {
Some(ref val) => val.to_sql(ty, out, ctx), Some(ref val) => val.to_sql(ty, out, ctx),
None => Ok(IsNull::Yes), None => Ok(IsNull::Yes),
@ -614,55 +539,35 @@ impl<T: ToSql> ToSql for Option<T> {
fn accepts(ty: &Type) -> bool { fn accepts(ty: &Type) -> bool {
<T as ToSql>::accepts(ty) <T as ToSql>::accepts(ty)
} }
}
impl ToSql for bool {
to_sql_checked!(); to_sql_checked!();
fn to_sql<W: Write + ?Sized>(&self,
_: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
try!(w.write_u8(*self as u8));
Ok(IsNull::No)
}
accepts!(Type::Bool);
} }
impl<'a, T: ToSql> ToSql for &'a [T] { impl<'a, T: ToSql> ToSql for &'a [T] {
to_sql_checked!(); fn to_sql(&self, ty: &Type, w: &mut Vec<u8>, ctx: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql<W: Write + ?Sized>(&self,
ty: &Type,
mut w: &mut W,
ctx: &SessionInfo)
-> Result<IsNull> {
let member_type = match *ty.kind() { let member_type = match *ty.kind() {
Kind::Array(ref member) => member, Kind::Array(ref member) => member,
_ => panic!("expected array type"), _ => panic!("expected array type"),
}; };
try!(w.write_i32::<BigEndian>(1)); // number of dimensions let dimensions = [
try!(w.write_i32::<BigEndian>(1)); // has nulls ArrayDimension {
try!(w.write_u32::<BigEndian>(member_type.oid())); len: try!(downcast(self.len())),
lower_bound: 1,
try!(w.write_i32::<BigEndian>(try!(downcast(self.len()))));
try!(w.write_i32::<BigEndian>(1)); // index offset
let mut inner_buf = vec![];
for e in *self {
match try!(e.to_sql(&member_type, &mut inner_buf, ctx)) {
IsNull::No => {
try!(w.write_i32::<BigEndian>(try!(downcast(inner_buf.len()))));
try!(w.write_all(&inner_buf));
}
IsNull::Yes => try!(w.write_i32::<BigEndian>(-1)),
} }
inner_buf.clear(); ];
}
try!(types::array_to_sql(dimensions.iter().cloned(),
true,
member_type.oid(),
self.iter(),
|e, w| {
match try!(e.to_sql(member_type, w, ctx)) {
IsNull::No => Ok(types::IsNull::No),
IsNull::Yes => Ok(types::IsNull::Yes),
}
},
w));
Ok(IsNull::No) Ok(IsNull::No)
} }
@ -672,48 +577,48 @@ impl<'a, T: ToSql> ToSql for &'a [T] {
_ => false, _ => false,
} }
} }
to_sql_checked!();
} }
impl<'a> ToSql for &'a [u8] { impl<'a> ToSql for &'a [u8] {
to_sql_checked!(); fn to_sql(&self, _: &Type, w: &mut Vec<u8>, _: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
types::bytea_to_sql(*self, w);
fn to_sql<W: Write + ?Sized>(&self, _: &Type, w: &mut W, _: &SessionInfo) -> Result<IsNull> {
try!(w.write_all(*self));
Ok(IsNull::No) Ok(IsNull::No)
} }
accepts!(Type::Bytea); accepts!(Type::Bytea);
to_sql_checked!();
} }
impl<T: ToSql> ToSql for Vec<T> { impl<T: ToSql> ToSql for Vec<T> {
to_sql_checked!(); fn to_sql(&self, ty: &Type, w: &mut Vec<u8>, ctx: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql<W: Write + ?Sized>(&self, ty: &Type, w: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
<&[T] as ToSql>::to_sql(&&**self, ty, w, ctx) <&[T] as ToSql>::to_sql(&&**self, ty, w, ctx)
} }
fn accepts(ty: &Type) -> bool { fn accepts(ty: &Type) -> bool {
<&[T] as ToSql>::accepts(ty) <&[T] as ToSql>::accepts(ty)
} }
to_sql_checked!();
} }
impl ToSql for Vec<u8> { impl ToSql for Vec<u8> {
to_sql_checked!(); fn to_sql(&self, ty: &Type, w: &mut Vec<u8>, ctx: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql<W: Write + ?Sized>(&self, ty: &Type, w: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
<&[u8] as ToSql>::to_sql(&&**self, ty, w, ctx) <&[u8] as ToSql>::to_sql(&&**self, ty, w, ctx)
} }
fn accepts(ty: &Type) -> bool { fn accepts(ty: &Type) -> bool {
<&[u8] as ToSql>::accepts(ty) <&[u8] as ToSql>::accepts(ty)
} }
to_sql_checked!();
} }
impl<'a> ToSql for &'a str { impl<'a> ToSql for &'a str {
to_sql_checked!(); fn to_sql(&self, _: &Type, w: &mut Vec<u8>, _: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
types::text_to_sql(*self, w);
fn to_sql<W: Write + ?Sized>(&self, _: &Type, w: &mut W, _: &SessionInfo) -> Result<IsNull> {
try!(w.write_all(self.as_bytes()));
Ok(IsNull::No) Ok(IsNull::No)
} }
@ -724,81 +629,49 @@ impl<'a> ToSql for &'a str {
_ => false, _ => false,
} }
} }
to_sql_checked!();
} }
impl ToSql for String { impl ToSql for String {
to_sql_checked!(); fn to_sql(&self, ty: &Type, w: &mut Vec<u8>, ctx: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql<W: Write + ?Sized>(&self, ty: &Type, w: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
<&str as ToSql>::to_sql(&&**self, ty, w, ctx) <&str as ToSql>::to_sql(&&**self, ty, w, ctx)
} }
fn accepts(ty: &Type) -> bool { fn accepts(ty: &Type) -> bool {
<&str as ToSql>::accepts(ty) <&str as ToSql>::accepts(ty)
} }
}
impl ToSql for i8 {
to_sql_checked!(); to_sql_checked!();
fn to_sql<W: Write + ?Sized>(&self,
_: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
try!(w.write_i8(*self));
Ok(IsNull::No)
}
accepts!(Type::Char);
} }
macro_rules! to_primitive { macro_rules! simple_to {
($t:ty, $f:ident, $($expected:pat),+) => { ($t:ty, $f:ident, $($expected:pat),+) => {
impl ToSql for $t { impl ToSql for $t {
to_sql_checked!(); fn to_sql(&self, _: &Type, w: &mut Vec<u8>, _: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
types::$f(*self, w);
fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo)
-> Result<IsNull> {
try!(w.$f::<BigEndian>(*self));
Ok(IsNull::No) Ok(IsNull::No)
} }
accepts!($($expected),+); accepts!($($expected),+);
to_sql_checked!();
} }
} }
} }
to_primitive!(i16, write_i16, Type::Int2); simple_to!(bool, bool_to_sql, Type::Bool);
to_primitive!(i32, write_i32, Type::Int4); simple_to!(i8, char_to_sql, Type::Char);
to_primitive!(u32, write_u32, Type::Oid); simple_to!(i16, int2_to_sql, Type::Int2);
to_primitive!(i64, write_i64, Type::Int8); simple_to!(i32, int4_to_sql, Type::Int4);
to_primitive!(f32, write_f32, Type::Float4); simple_to!(u32, oid_to_sql, Type::Oid);
to_primitive!(f64, write_f64, Type::Float8); simple_to!(i64, int8_to_sql, Type::Int8);
simple_to!(f32, float4_to_sql, Type::Float4);
simple_to!(f64, float8_to_sql, Type::Float8);
impl ToSql for HashMap<String, Option<String>> { impl ToSql for HashMap<String, Option<String>> {
to_sql_checked!(); fn to_sql(&self, _: &Type, w: &mut Vec<u8>, _: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
try!(types::hstore_to_sql(self.iter().map(|(k, v)| (&**k, v.as_ref().map(|v| &**v))), w));
fn to_sql<W: Write + ?Sized>(&self,
_: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
try!(w.write_i32::<BigEndian>(try!(downcast(self.len()))));
for (key, val) in self {
try!(w.write_i32::<BigEndian>(try!(downcast(key.len()))));
try!(w.write_all(key.as_bytes()));
match *val {
Some(ref val) => {
try!(w.write_i32::<BigEndian>(try!(downcast(val.len()))));
try!(w.write_all(val.as_bytes()));
}
None => try!(w.write_i32::<BigEndian>(-1)),
}
}
Ok(IsNull::No) Ok(IsNull::No)
} }
@ -808,11 +681,13 @@ impl ToSql for HashMap<String, Option<String>> {
_ => false, _ => false,
} }
} }
to_sql_checked!();
} }
fn downcast(len: usize) -> Result<i32> { fn downcast(len: usize) -> Result<i32, Box<Error + Sync + Send>> {
if len > i32::max_value() as usize { if len > i32::max_value() as usize {
Err(Error::Conversion("value too large to transmit".into())) Err("value too large to transmit".into())
} else { } else {
Ok(len as i32) Ok(len as i32)
} }

View File

@ -1,39 +1,33 @@
extern crate rustc_serialize; extern crate rustc_serialize;
use self::rustc_serialize::json; use self::rustc_serialize::json;
use std::io::prelude::*; use std::io::{Cursor, Write};
use byteorder::{ReadBytesExt, WriteBytesExt}; use byteorder::ReadBytesExt;
use std::error::Error;
use Result;
use error::Error;
use types::{FromSql, ToSql, IsNull, Type, SessionInfo}; use types::{FromSql, ToSql, IsNull, Type, SessionInfo};
impl FromSql for json::Json { impl FromSql for json::Json {
fn from_sql<R: Read>(ty: &Type, raw: &mut R, _: &SessionInfo) -> Result<json::Json> { fn from_sql(ty: &Type, raw: &[u8], _: &SessionInfo) -> Result<json::Json, Box<Error + Sync + Send>> {
let mut raw = Cursor::new(raw);
if let Type::Jsonb = *ty { if let Type::Jsonb = *ty {
// We only support version 1 of the jsonb binary format // We only support version 1 of the jsonb binary format
if try!(raw.read_u8()) != 1 { if try!(raw.read_u8()) != 1 {
return Err(Error::Conversion("unsupported JSONB encoding version".into())); return Err("unsupported JSONB encoding version".into());
} }
} }
json::Json::from_reader(raw).map_err(|err| Error::Conversion(Box::new(err))) json::Json::from_reader(&mut raw).map_err(Into::into)
} }
accepts!(Type::Json, Type::Jsonb); accepts!(Type::Json, Type::Jsonb);
} }
impl ToSql for json::Json { impl ToSql for json::Json {
fn to_sql<W: Write + ?Sized>(&self, fn to_sql(&self, ty: &Type, mut out: &mut Vec<u8>, _: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
ty: &Type,
mut out: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
if let Type::Jsonb = *ty { if let Type::Jsonb = *ty {
try!(out.write_u8(1)); out.push(1);
} }
try!(write!(out, "{}", self)); try!(write!(out, "{}", self));
Ok(IsNull::No) Ok(IsNull::No)
} }

View File

@ -1,39 +1,32 @@
extern crate serde_json; extern crate serde_json;
use std::io::prelude::*; use byteorder::ReadBytesExt;
use byteorder::{ReadBytesExt, WriteBytesExt};
use self::serde_json::Value; use self::serde_json::Value;
use std::error::Error;
use std::io::Write;
use Result;
use error::Error;
use types::{FromSql, ToSql, IsNull, Type, SessionInfo}; use types::{FromSql, ToSql, IsNull, Type, SessionInfo};
impl FromSql for Value { impl FromSql for Value {
fn from_sql<R: Read>(ty: &Type, raw: &mut R, _: &SessionInfo) -> Result<Value> { fn from_sql(ty: &Type, mut raw: &[u8], _: &SessionInfo) -> Result<Value, Box<Error + Sync + Send>> {
if let Type::Jsonb = *ty { if let Type::Jsonb = *ty {
// We only support version 1 of the jsonb binary format // We only support version 1 of the jsonb binary format
if try!(raw.read_u8()) != 1 { if try!(raw.read_u8()) != 1 {
return Err(Error::Conversion("unsupported JSONB encoding version".into())); return Err("unsupported JSONB encoding version".into());
} }
} }
serde_json::de::from_reader(raw).map_err(|err| Error::Conversion(Box::new(err))) serde_json::de::from_reader(raw).map_err(Into::into)
} }
accepts!(Type::Json, Type::Jsonb); accepts!(Type::Json, Type::Jsonb);
} }
impl ToSql for Value { impl ToSql for Value {
fn to_sql<W: Write + ?Sized>(&self, fn to_sql(&self, ty: &Type, mut out: &mut Vec<u8>, _: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
ty: &Type,
mut out: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
if let Type::Jsonb = *ty { if let Type::Jsonb = *ty {
try!(out.write_u8(1)); out.push(1);
} }
try!(write!(out, "{:?}", self)); try!(write!(out, "{:?}", self));
Ok(IsNull::No) Ok(IsNull::No)
} }

View File

@ -1,10 +1,7 @@
use std::io::prelude::*; use postgres_protocol::types;
use std::{i32, i64}; use std::{i32, i64};
use std::error::Error;
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
use Result;
use error::Error;
use types::{Type, FromSql, ToSql, IsNull, SessionInfo}; use types::{Type, FromSql, ToSql, IsNull, SessionInfo};
/// A wrapper that can be used to represent infinity with `Type::Date` types. /// A wrapper that can be used to represent infinity with `Type::Date` types.
@ -19,18 +16,11 @@ pub enum Date<T> {
} }
impl<T: FromSql> FromSql for Date<T> { impl<T: FromSql> FromSql for Date<T> {
fn from_sql<R: Read>(ty: &Type, raw: &mut R, ctx: &SessionInfo) -> Result<Self> { fn from_sql(ty: &Type, raw: &[u8], ctx: &SessionInfo) -> Result<Self, Box<Error + Sync + Send>> {
if *ty != Type::Date { match try!(types::date_from_sql(raw)) {
return Err(Error::Conversion("expected date type".into()));
}
let mut buf = [0; 4];
try!(raw.read_exact(buf.as_mut()));
match try!(buf.as_ref().read_i32::<BigEndian>()) {
i32::MAX => Ok(Date::PosInfinity), i32::MAX => Ok(Date::PosInfinity),
i32::MIN => Ok(Date::NegInfinity), i32::MIN => Ok(Date::NegInfinity),
_ => T::from_sql(ty, &mut &mut buf.as_ref(), ctx).map(Date::Value), _ => T::from_sql(ty, raw, ctx).map(Date::Value),
} }
} }
@ -39,22 +29,14 @@ impl<T: FromSql> FromSql for Date<T> {
} }
} }
impl<T: ToSql> ToSql for Date<T> { impl<T: ToSql> ToSql for Date<T> {
fn to_sql<W: Write + ?Sized>(&self, fn to_sql(&self, ty: &Type, out: &mut Vec<u8>, ctx: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
ty: &Type,
out: &mut W,
ctx: &SessionInfo)
-> Result<IsNull> {
if *ty != Type::Date {
return Err(Error::Conversion("expected date type".into()));
}
let value = match *self { let value = match *self {
Date::PosInfinity => i32::MAX, Date::PosInfinity => i32::MAX,
Date::NegInfinity => i32::MIN, Date::NegInfinity => i32::MIN,
Date::Value(ref v) => return v.to_sql(ty, out, ctx), Date::Value(ref v) => return v.to_sql(ty, out, ctx),
}; };
try!(out.write_i32::<BigEndian>(value)); types::date_to_sql(value, out);
Ok(IsNull::No) Ok(IsNull::No)
} }
@ -78,18 +60,11 @@ pub enum Timestamp<T> {
} }
impl<T: FromSql> FromSql for Timestamp<T> { impl<T: FromSql> FromSql for Timestamp<T> {
fn from_sql<R: Read>(ty: &Type, raw: &mut R, ctx: &SessionInfo) -> Result<Self> { fn from_sql(ty: &Type, raw: &[u8], ctx: &SessionInfo) -> Result<Self, Box<Error + Sync + Send>> {
if *ty != Type::Timestamp && *ty != Type::Timestamptz { match try!(types::timestamp_from_sql(raw)) {
return Err(Error::Conversion("expected timestamp or timestamptz type".into()));
}
let mut buf = [0; 8];
try!(raw.read_exact(buf.as_mut()));
match try!(buf.as_ref().read_i64::<BigEndian>()) {
i64::MAX => Ok(Timestamp::PosInfinity), i64::MAX => Ok(Timestamp::PosInfinity),
i64::MIN => Ok(Timestamp::NegInfinity), i64::MIN => Ok(Timestamp::NegInfinity),
_ => T::from_sql(ty, &mut &mut buf.as_ref(), ctx).map(Timestamp::Value), _ => T::from_sql(ty, raw, ctx).map(Timestamp::Value),
} }
} }
@ -99,22 +74,14 @@ impl<T: FromSql> FromSql for Timestamp<T> {
} }
impl<T: ToSql> ToSql for Timestamp<T> { impl<T: ToSql> ToSql for Timestamp<T> {
fn to_sql<W: Write + ?Sized>(&self, fn to_sql(&self, ty: &Type, out: &mut Vec<u8>, ctx: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
ty: &Type,
out: &mut W,
ctx: &SessionInfo)
-> Result<IsNull> {
if *ty != Type::Timestamp && *ty != Type::Timestamptz {
return Err(Error::Conversion("expected timestamp or timestamptz type".into()));
}
let value = match *self { let value = match *self {
Timestamp::PosInfinity => i64::MAX, Timestamp::PosInfinity => i64::MAX,
Timestamp::NegInfinity => i64::MIN, Timestamp::NegInfinity => i64::MIN,
Timestamp::Value(ref v) => return v.to_sql(ty, out, ctx), Timestamp::Value(ref v) => return v.to_sql(ty, out, ctx),
}; };
try!(out.write_i64::<BigEndian>(value)); types::timestamp_to_sql(value, out);
Ok(IsNull::No) Ok(IsNull::No)
} }

View File

@ -1,10 +1,9 @@
extern crate time; extern crate time;
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
use self::time::Timespec; use self::time::Timespec;
use std::io::prelude::*; use std::error::Error;
use postgres_protocol::types;
use Result;
use types::{Type, FromSql, ToSql, IsNull, SessionInfo}; use types::{Type, FromSql, ToSql, IsNull, SessionInfo};
const USEC_PER_SEC: i64 = 1_000_000; const USEC_PER_SEC: i64 = 1_000_000;
@ -14,8 +13,8 @@ const NSEC_PER_USEC: i64 = 1_000;
const TIME_SEC_CONVERSION: i64 = 946684800; const TIME_SEC_CONVERSION: i64 = 946684800;
impl FromSql for Timespec { impl FromSql for Timespec {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<Timespec> { fn from_sql(_: &Type, raw: &[u8], _: &SessionInfo) -> Result<Timespec, Box<Error + Sync + Send>> {
let t = try!(raw.read_i64::<BigEndian>()); let t = try!(types::timestamp_from_sql(raw));
let mut sec = t / USEC_PER_SEC + TIME_SEC_CONVERSION; let mut sec = t / USEC_PER_SEC + TIME_SEC_CONVERSION;
let mut usec = t % USEC_PER_SEC; let mut usec = t % USEC_PER_SEC;
@ -31,13 +30,9 @@ impl FromSql for Timespec {
} }
impl ToSql for Timespec { impl ToSql for Timespec {
fn to_sql<W: Write + ?Sized>(&self, fn to_sql(&self, _: &Type, w: &mut Vec<u8>, _: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
_: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
let t = (self.sec - TIME_SEC_CONVERSION) * USEC_PER_SEC + self.nsec as i64 / NSEC_PER_USEC; let t = (self.sec - TIME_SEC_CONVERSION) * USEC_PER_SEC + self.nsec as i64 / NSEC_PER_USEC;
try!(w.write_i64::<BigEndian>(t)); types::timestamp_to_sql(t, w);
Ok(IsNull::No) Ok(IsNull::No)
} }

View File

@ -1,15 +1,14 @@
extern crate uuid; extern crate uuid;
use std::io::prelude::*; use postgres_protocol::types;
use self::uuid::Uuid; use self::uuid::Uuid;
use std::error::Error;
use types::{FromSql, ToSql, Type, IsNull, SessionInfo}; use types::{FromSql, ToSql, Type, IsNull, SessionInfo};
use Result;
impl FromSql for Uuid { impl FromSql for Uuid {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<Uuid> { fn from_sql(_: &Type, raw: &[u8], _: &SessionInfo) -> Result<Uuid, Box<Error + Sync + Send>> {
let mut bytes = [0; 16]; let bytes = try!(types::uuid_from_sql(raw));
try!(raw.read_exact(&mut bytes));
Ok(Uuid::from_bytes(&bytes).unwrap()) Ok(Uuid::from_bytes(&bytes).unwrap())
} }
@ -17,8 +16,8 @@ impl FromSql for Uuid {
} }
impl ToSql for Uuid { impl ToSql for Uuid {
fn to_sql<W: Write + ?Sized>(&self, _: &Type, w: &mut W, _: &SessionInfo) -> Result<IsNull> { fn to_sql(&self, _: &Type, w: &mut Vec<u8>, _: &SessionInfo) -> Result<IsNull, Box<Error + Sync + Send>> {
try!(w.write_all(self.as_bytes())); types::uuid_to_sql(*self.as_bytes(), w);
Ok(IsNull::No) Ok(IsNull::No)
} }

View File

@ -1,8 +1,10 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::error;
use std::f32; use std::f32;
use std::f64; use std::f64;
use std::fmt; use std::fmt;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::result;
use postgres::{Connection, TlsMode, Result}; use postgres::{Connection, TlsMode, Result};
use postgres::error::Error; use postgres::error::Error;
@ -251,9 +253,7 @@ fn domain() {
struct SessionId(Vec<u8>); struct SessionId(Vec<u8>);
impl ToSql for SessionId { impl ToSql for SessionId {
fn to_sql<W: ?Sized>(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) -> Result<IsNull> fn to_sql(&self, ty: &Type, out: &mut Vec<u8>, ctx: &SessionInfo) -> result::Result<IsNull, Box<error::Error + Sync + Send>> {
where W: Write
{
let inner = match *ty.kind() { let inner = match *ty.kind() {
Kind::Domain(ref inner) => inner, Kind::Domain(ref inner) => inner,
_ => unreachable!(), _ => unreachable!(),
@ -269,7 +269,7 @@ fn domain() {
} }
impl FromSql for SessionId { impl FromSql for SessionId {
fn from_sql<R: Read>(ty: &Type, raw: &mut R, ctx: &SessionInfo) -> Result<Self> { fn from_sql(ty: &Type, raw: &[u8], ctx: &SessionInfo) -> result::Result<Self, Box<error::Error + Sync + Send>> {
Vec::<u8>::from_sql(ty, raw, ctx).map(SessionId) Vec::<u8>::from_sql(ty, raw, ctx).map(SessionId)
} }