Redesign ToSql and remove RawToSql
This commit is contained in:
parent
2380165c86
commit
1767661618
25
src/lib.rs
25
src/lib.rs
@ -76,6 +76,7 @@ use url::Url;
|
||||
pub use error::{Error, ConnectError, SqlState, DbError, ErrorPosition};
|
||||
#[doc(inline)]
|
||||
pub use types::{Oid, Type, Kind, ToSql, FromSql};
|
||||
use types::IsNull;
|
||||
#[doc(inline)]
|
||||
pub use types::Slice;
|
||||
use io::{InternalStream, Timeout};
|
||||
@ -727,12 +728,17 @@ impl InnerConnection {
|
||||
}
|
||||
|
||||
// Ew @ doing this manually :(
|
||||
let mut buf = vec![];
|
||||
let value = match try!(oid.to_sql_checked(&Type::Oid, &mut buf)) {
|
||||
IsNull::Yes => None,
|
||||
IsNull::No => Some(buf),
|
||||
};
|
||||
try!(self.write_messages(&[
|
||||
Bind {
|
||||
portal: "",
|
||||
statement: TYPEINFO_QUERY,
|
||||
formats: &[1],
|
||||
values: &[try!(oid.to_sql(&Type::Oid))],
|
||||
values: &[value],
|
||||
result_formats: &[1]
|
||||
},
|
||||
Execute {
|
||||
@ -1319,7 +1325,11 @@ impl<'conn> Statement<'conn> {
|
||||
params.len());
|
||||
let mut values = vec![];
|
||||
for (param, ty) in params.iter().zip(self.param_types.iter()) {
|
||||
values.push(try!(param.to_sql(ty)));
|
||||
let mut buf = vec![];
|
||||
match try!(param.to_sql_checked(ty, &mut buf)) {
|
||||
IsNull::Yes => values.push(None),
|
||||
IsNull::No => values.push(Some(buf)),
|
||||
}
|
||||
};
|
||||
|
||||
try!(conn.write_messages(&[
|
||||
@ -2054,13 +2064,14 @@ impl<'a> CopyInStatement<'a> {
|
||||
loop {
|
||||
match (row.next(), types.next()) {
|
||||
(Some(val), Some(ty)) => {
|
||||
match val.to_sql(ty) {
|
||||
Ok(None) => {
|
||||
let mut inner_buf = vec![];
|
||||
match val.to_sql_checked(ty, &mut inner_buf) {
|
||||
Ok(IsNull::Yes) => {
|
||||
let _ = buf.write_be_i32(-1);
|
||||
}
|
||||
Ok(Some(val)) => {
|
||||
let _ = buf.write_be_i32(val.len() as i32);
|
||||
let _ = buf.write_all(&val);
|
||||
Ok(IsNull::No) => {
|
||||
let _ = buf.write_be_i32(inner_buf.len() as i32);
|
||||
let _ = buf.write_all(&inner_buf);
|
||||
}
|
||||
Err(err) => {
|
||||
// FIXME this is not the right way to handle this
|
||||
|
@ -1,7 +1,7 @@
|
||||
use serialize::json;
|
||||
|
||||
use {Result, Error};
|
||||
use types::{FromSql, RawToSql, Type};
|
||||
use types::{FromSql, ToSql, IsNull, Type};
|
||||
|
||||
impl FromSql for json::Json {
|
||||
fn from_sql<R: Reader>(ty: &Type, raw: &mut R) -> Result<json::Json> {
|
||||
@ -17,14 +17,17 @@ impl FromSql for json::Json {
|
||||
accepts!(Type::Json, Type::Jsonb);
|
||||
}
|
||||
|
||||
impl RawToSql for json::Json {
|
||||
fn raw_to_sql<W: Writer>(&self, ty: &Type, raw: &mut W) -> Result<()> {
|
||||
impl ToSql for json::Json {
|
||||
fn to_sql<W: Writer+?Sized>(&self, ty: &Type, out: &mut W) -> Result<IsNull> {
|
||||
if let Type::Jsonb = *ty {
|
||||
try!(raw.write_u8(1));
|
||||
try!(out.write_u8(1));
|
||||
}
|
||||
|
||||
Ok(try!(write!(raw, "{}", self)))
|
||||
try!(write!(out, "{}", self));
|
||||
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
accepts!(Type::Json, Type::Jsonb);
|
||||
to_sql_checked!();
|
||||
}
|
||||
|
||||
to_raw_to_impl!(Type::Json, Type::Jsonb; json::Json);
|
||||
|
394
src/types/mod.rs
394
src/types/mod.rs
@ -10,6 +10,7 @@ use error::Error;
|
||||
|
||||
pub use ugh_privacy::Other;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! accepts {
|
||||
($($expected:pat),+) => (
|
||||
fn accepts(ty: &::types::Type) -> bool {
|
||||
@ -21,124 +22,16 @@ macro_rules! accepts {
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! check_types {
|
||||
($($expected:pat),+; $actual:ident) => (
|
||||
match $actual {
|
||||
$(&$expected)|+ => {}
|
||||
actual => return Err(::Error::WrongType(actual.clone()))
|
||||
#[macro_export]
|
||||
macro_rules! to_sql_checked {
|
||||
() => {
|
||||
fn to_sql_checked(&self, ty: &Type, out: &mut Writer) -> Result<IsNull> {
|
||||
if !<Self as ToSql>::accepts(ty) {
|
||||
return Err($crate::Error::WrongType(ty.clone()));
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! raw_from_impl {
|
||||
($t:ty, $f:ident) => (
|
||||
impl RawFromSql for $t {
|
||||
fn raw_from_sql<R: Reader>(_: &Type, raw: &mut R) -> Result<$t> {
|
||||
Ok(try!(raw.$f()))
|
||||
self.to_sql(ty, out)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! from_option_impl {
|
||||
($t:ty) => {
|
||||
impl ::types::FromSql for $t {
|
||||
fn from_sql(ty: &Type, raw: Option<&[u8]>) -> Result<$t> {
|
||||
use Error;
|
||||
use types::FromSql;
|
||||
|
||||
// FIXME when you can specify Self types properly
|
||||
let ret: Result<Option<$t>> = FromSql::from_sql(ty, raw);
|
||||
match ret {
|
||||
Ok(Some(val)) => Ok(val),
|
||||
Ok(None) => Err(Error::WasNull),
|
||||
Err(err) => Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! from_map_impl {
|
||||
($($expected:pat),+; $t:ty, $blk:expr) => (
|
||||
impl ::types::FromSql for Option<$t> {
|
||||
fn from_sql(ty: &Type, raw: Option<&[u8]>) -> Result<Option<$t>> {
|
||||
check_types!($($expected),+; ty);
|
||||
match raw {
|
||||
Some(buf) => ($blk)(ty, buf).map(|ok| Some(ok)),
|
||||
None => Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
from_option_impl!($t);
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! from_raw_from_impl {
|
||||
($($expected:pat),+; $t:ty) => (
|
||||
from_map_impl!($($expected),+; $t, |ty, mut buf: &[u8]| {
|
||||
use types::RawFromSql;
|
||||
|
||||
RawFromSql::raw_from_sql(ty, &mut buf)
|
||||
});
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! raw_to_impl {
|
||||
($t:ty, $f:ident) => (
|
||||
impl RawToSql for $t {
|
||||
fn raw_to_sql<W: Writer>(&self, _: &Type, w: &mut W) -> Result<()> {
|
||||
Ok(try!(w.$f(*self)))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! to_option_impl {
|
||||
($($oid:pat),+; $t:ty) => (
|
||||
impl ::types::ToSql for Option<$t> {
|
||||
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
|
||||
check_types!($($oid),+; ty);
|
||||
|
||||
match *self {
|
||||
None => Ok(None),
|
||||
Some(ref val) => val.to_sql(ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! to_option_impl_lifetime {
|
||||
($($oid:pat),+; $t:ty) => (
|
||||
impl<'a> ToSql for Option<$t> {
|
||||
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
|
||||
check_types!($($oid),+; ty);
|
||||
|
||||
match *self {
|
||||
None => Ok(None),
|
||||
Some(ref val) => val.to_sql(ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! to_raw_to_impl {
|
||||
($($oid:pat),+; $t:ty) => (
|
||||
impl ::types::ToSql for $t {
|
||||
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
|
||||
check_types!($($oid),+; ty);
|
||||
|
||||
let mut writer = vec![];
|
||||
try!(self.raw_to_sql(ty, &mut writer));
|
||||
Ok(Some(writer))
|
||||
}
|
||||
}
|
||||
|
||||
to_option_impl!($($oid),+; $t);
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "uuid")]
|
||||
@ -712,54 +605,146 @@ impl FromSql for HashMap<String, Option<String>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for types that can be converted into Postgres values
|
||||
/// An enum representing the nullability of a Postgres value.
|
||||
pub enum IsNull {
|
||||
/// The value is NULL.
|
||||
Yes,
|
||||
/// The value is not NULL.
|
||||
No,
|
||||
}
|
||||
|
||||
/// A trait for types that can be converted into Postgres values.
|
||||
pub trait ToSql {
|
||||
/// Converts the value of `self` into the binary format appropriate for the
|
||||
/// Postgres backend.
|
||||
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>>;
|
||||
}
|
||||
|
||||
/// A utility trait used by `ToSql` implementations.
|
||||
pub trait RawToSql {
|
||||
/// Converts the value of `self` into the binary format of the specified
|
||||
/// Postgres type, writing it to `w`.
|
||||
/// Converts the value of `self` into Postgres data.
|
||||
///
|
||||
/// It is the caller's responsibility to make sure that this type can be
|
||||
/// converted to the specified Postgres type.
|
||||
fn raw_to_sql<W: Writer>(&self, ty: &Type, w: &mut W) -> Result<()>;
|
||||
/// The caller of this method is responsible for ensuring that this type
|
||||
/// is compatible with the Postgres `Type`.
|
||||
///
|
||||
/// The return value indicates if this value should be represented as
|
||||
/// `NULL`. If this is the case, implementations **must not** write
|
||||
/// anything to `out`.
|
||||
fn to_sql<W: ?Sized>(&self, ty: &Type, out: &mut W) -> Result<IsNull>
|
||||
where Self: Sized, W: Writer;
|
||||
|
||||
/// Determines if a value of this type can be converted to the specified
|
||||
/// Postgres `Type`.
|
||||
fn accepts(ty: &Type) -> bool where Self: Sized;
|
||||
|
||||
/// An adaptor method used internally by Rust-Postgres.
|
||||
///
|
||||
/// *All* implementations of this method should be generated by the
|
||||
/// `to_sql_checked!()` macro.
|
||||
fn to_sql_checked(&self, ty: &Type, out: &mut Writer) -> Result<IsNull>;
|
||||
}
|
||||
|
||||
impl RawToSql for bool {
|
||||
fn raw_to_sql<W: Writer>(&self, _: &Type, w: &mut W) -> Result<()> {
|
||||
Ok(try!(w.write_u8(*self as u8)))
|
||||
impl<T: ToSql> ToSql for Option<T> {
|
||||
to_sql_checked!();
|
||||
|
||||
fn to_sql<W: Writer+?Sized>(&self, ty: &Type, out: &mut W) -> Result<IsNull> {
|
||||
match *self {
|
||||
Some(ref val) => val.to_sql(ty, out),
|
||||
None => Ok(IsNull::Yes),
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
<T as ToSql>::accepts(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl RawToSql for Vec<u8> {
|
||||
fn raw_to_sql<W: Writer>(&self, _: &Type, w: &mut W) -> Result<()> {
|
||||
Ok(try!(w.write_all(&**self)))
|
||||
impl ToSql for bool {
|
||||
to_sql_checked!();
|
||||
|
||||
fn to_sql<W: Writer+?Sized>(&self, _: &Type, w: &mut W) -> Result<IsNull> {
|
||||
try!(w.write_u8(*self as u8));
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
accepts!(Type::Bool);
|
||||
}
|
||||
|
||||
impl<'a> ToSql for &'a [u8] {
|
||||
to_sql_checked!();
|
||||
|
||||
fn to_sql<W: Writer+?Sized>(&self, _: &Type, w: &mut W) -> Result<IsNull> {
|
||||
try!(w.write_all(*self));
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
accepts!(Type::Bytea);
|
||||
}
|
||||
|
||||
impl ToSql for Vec<u8> {
|
||||
to_sql_checked!();
|
||||
|
||||
fn to_sql<W: Writer+?Sized>(&self, ty: &Type, w: &mut W) -> Result<IsNull> {
|
||||
(&**self).to_sql(ty, w)
|
||||
}
|
||||
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
<&[u8] as ToSql>::accepts(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl RawToSql for String {
|
||||
fn raw_to_sql<W: Writer>(&self, _: &Type, w: &mut W) -> Result<()> {
|
||||
Ok(try!(w.write_all(self.as_bytes())))
|
||||
impl<'a> ToSql for &'a str {
|
||||
to_sql_checked!();
|
||||
|
||||
fn to_sql<W: Writer+?Sized>(&self, _: &Type, w: &mut W) -> Result<IsNull> {
|
||||
try!(w.write_all(self.as_bytes()));
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
match *ty {
|
||||
Type::Varchar | Type::Text | Type::Bpchar | Type::Name => true,
|
||||
Type::Other(ref u) if u.name() == "citext" => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raw_to_impl!(i8, write_i8);
|
||||
raw_to_impl!(i16, write_be_i16);
|
||||
raw_to_impl!(i32, write_be_i32);
|
||||
raw_to_impl!(u32, write_be_u32);
|
||||
raw_to_impl!(i64, write_be_i64);
|
||||
raw_to_impl!(f32, write_be_f32);
|
||||
raw_to_impl!(f64, write_be_f64);
|
||||
impl ToSql for String {
|
||||
to_sql_checked!();
|
||||
|
||||
impl RawToSql for IpAddr {
|
||||
fn raw_to_sql<W: Writer>(&self, _: &Type, raw: &mut W) -> Result<()> {
|
||||
fn to_sql<W: Writer+?Sized>(&self, ty: &Type, w: &mut W) -> Result<IsNull> {
|
||||
(&**self).to_sql(ty, w)
|
||||
}
|
||||
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
<&str as ToSql>::accepts(ty)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! to_primitive {
|
||||
($t:ty, $f:ident, $($expected:pat),+) => {
|
||||
impl ToSql for $t {
|
||||
to_sql_checked!();
|
||||
|
||||
fn to_sql<W: Writer+?Sized>(&self, _: &Type, w: &mut W) -> Result<IsNull> {
|
||||
try!(w.$f(*self));
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
accepts!($($expected),+);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
to_primitive!(i8, write_i8, Type::Char);
|
||||
to_primitive!(i16, write_be_i16, Type::Int2);
|
||||
to_primitive!(i32, write_be_i32, Type::Int4);
|
||||
to_primitive!(u32, write_be_u32, Type::Oid);
|
||||
to_primitive!(i64, write_be_i64, Type::Int8);
|
||||
to_primitive!(f32, write_be_f32, Type::Float4);
|
||||
to_primitive!(f64, write_be_f64, Type::Float8);
|
||||
|
||||
impl ToSql for IpAddr {
|
||||
to_sql_checked!();
|
||||
|
||||
fn to_sql<W: Writer+?Sized>(&self, _: &Type, w: &mut W) -> Result<IsNull> {
|
||||
match *self {
|
||||
IpAddr::Ipv4Addr(a, b, c, d) => {
|
||||
try!(raw.write_all(&[2, // family
|
||||
try!(w.write_all(&[2, // family
|
||||
32, // bits
|
||||
0, // is_cidr
|
||||
4, // nb
|
||||
@ -767,120 +752,53 @@ impl RawToSql for IpAddr {
|
||||
]));
|
||||
}
|
||||
IpAddr::Ipv6Addr(a, b, c, d, e, f, g, h) => {
|
||||
try!(raw.write_all(&[3, // family
|
||||
try!(w.write_all(&[3, // family
|
||||
128, // bits
|
||||
0, // is_cidr
|
||||
16, // nb
|
||||
]));
|
||||
try!(raw.write_be_u16(a));
|
||||
try!(raw.write_be_u16(b));
|
||||
try!(raw.write_be_u16(c));
|
||||
try!(raw.write_be_u16(d));
|
||||
try!(raw.write_be_u16(e));
|
||||
try!(raw.write_be_u16(f));
|
||||
try!(raw.write_be_u16(g));
|
||||
try!(raw.write_be_u16(h));
|
||||
try!(w.write_be_u16(a));
|
||||
try!(w.write_be_u16(b));
|
||||
try!(w.write_be_u16(c));
|
||||
try!(w.write_be_u16(d));
|
||||
try!(w.write_be_u16(e));
|
||||
try!(w.write_be_u16(f));
|
||||
try!(w.write_be_u16(g));
|
||||
try!(w.write_be_u16(h));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
accepts!(Type::Cidr, Type::Inet);
|
||||
}
|
||||
|
||||
to_raw_to_impl!(Type::Bool; bool);
|
||||
to_raw_to_impl!(Type::Bytea; Vec<u8>);
|
||||
to_raw_to_impl!(Type::Inet, Type::Cidr; IpAddr);
|
||||
to_raw_to_impl!(Type::Char; i8);
|
||||
to_raw_to_impl!(Type::Int2; i16);
|
||||
to_raw_to_impl!(Type::Int4; i32);
|
||||
to_raw_to_impl!(Type::Oid; u32);
|
||||
to_raw_to_impl!(Type::Int8; i64);
|
||||
to_raw_to_impl!(Type::Float4; f32);
|
||||
to_raw_to_impl!(Type::Float8; f64);
|
||||
|
||||
impl ToSql for String {
|
||||
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
|
||||
(&**self).to_sql(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for Option<String> {
|
||||
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
|
||||
self.as_ref().map(|s| &**s).to_sql(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToSql for &'a str {
|
||||
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
|
||||
match *ty {
|
||||
Type::Varchar | Type::Text | Type::Bpchar | Type::Name => {}
|
||||
Type::Other(ref u) if u.name() == "citext" => {}
|
||||
_ => return Err(Error::WrongType(ty.clone()))
|
||||
}
|
||||
Ok(Some(self.as_bytes().to_vec()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToSql for Option<&'a str> {
|
||||
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
|
||||
match *ty {
|
||||
Type::Varchar | Type::Text | Type::Bpchar | Type::Name => {}
|
||||
Type::Other(ref u) if u.name() == "citext" => {}
|
||||
_ => return Err(Error::WrongType(ty.clone()))
|
||||
}
|
||||
match *self {
|
||||
Some(ref val) => val.to_sql(ty),
|
||||
None => Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToSql for &'a [u8] {
|
||||
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
|
||||
check_types!(Type::Bytea; ty);
|
||||
Ok(Some(self.to_vec()))
|
||||
}
|
||||
}
|
||||
|
||||
to_option_impl_lifetime!(Type::Bytea; &'a [u8]);
|
||||
|
||||
impl ToSql for HashMap<String, Option<String>> {
|
||||
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
|
||||
match *ty {
|
||||
Type::Other(ref u) if u.name() == "hstore" => {}
|
||||
_ => return Err(Error::WrongType(ty.clone()))
|
||||
}
|
||||
to_sql_checked!();
|
||||
|
||||
let mut buf = vec![];
|
||||
|
||||
try!(buf.write_be_i32(self.len() as i32));
|
||||
fn to_sql<W: Writer+?Sized>(&self, _: &Type, w: &mut W) -> Result<IsNull> {
|
||||
try!(w.write_be_i32(self.len() as i32));
|
||||
|
||||
for (key, val) in self.iter() {
|
||||
try!(buf.write_be_i32(key.len() as i32));
|
||||
try!(buf.write_all(key.as_bytes()));
|
||||
try!(w.write_be_i32(key.len() as i32));
|
||||
try!(w.write_all(key.as_bytes()));
|
||||
|
||||
match *val {
|
||||
Some(ref val) => {
|
||||
try!(buf.write_be_i32(val.len() as i32));
|
||||
try!(buf.write_all(val.as_bytes()));
|
||||
try!(w.write_be_i32(val.len() as i32));
|
||||
try!(w.write_all(val.as_bytes()));
|
||||
}
|
||||
None => try!(buf.write_be_i32(-1))
|
||||
None => try!(w.write_be_i32(-1))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(buf))
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for Option<HashMap<String, Option<String>>> {
|
||||
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
match *ty {
|
||||
Type::Other(ref u) if u.name() == "hstore" => {}
|
||||
_ => return Err(Error::WrongType(ty.clone()))
|
||||
}
|
||||
|
||||
match *self {
|
||||
Some(ref inner) => inner.to_sql(ty),
|
||||
None => Ok(None)
|
||||
Type::Other(ref u) if u.name() == "hstore" => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
use byteorder::{BigEndian, WriterBytesExt};
|
||||
|
||||
use {Type, ToSql, Result, Error, Kind};
|
||||
use types::IsNull;
|
||||
|
||||
/// An adapter type mapping slices to Postgres arrays.
|
||||
///
|
||||
@ -25,32 +24,42 @@ use {Type, ToSql, Result, Error, Kind};
|
||||
pub struct Slice<'a, T: 'a + ToSql>(pub &'a [T]);
|
||||
|
||||
impl<'a, T: 'a + ToSql> ToSql for Slice<'a, T> {
|
||||
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
|
||||
to_sql_checked!();
|
||||
|
||||
fn to_sql<W: Writer+?Sized>(&self, ty: &Type, w: &mut W) -> Result<IsNull> {
|
||||
let member_type = match ty.kind() {
|
||||
&Kind::Array(ref member) => member,
|
||||
_ => return Err(Error::WrongType(ty.clone())),
|
||||
_ => panic!("expected array type"),
|
||||
};
|
||||
if !<T as ToSql>::accepts(&member_type) {
|
||||
return Err(Error::WrongType(ty.clone()));
|
||||
}
|
||||
|
||||
let mut buf = vec![];
|
||||
let _ = buf.write_i32::<BigEndian>(1); // number of dimensions
|
||||
let _ = buf.write_i32::<BigEndian>(1); // has nulls
|
||||
let _ = buf.write_u32::<BigEndian>(member_type.to_oid());
|
||||
try!(w.write_be_i32(1)); // number of dimensions
|
||||
try!(w.write_be_i32(1)); // has nulls
|
||||
try!(w.write_be_u32(member_type.to_oid()));
|
||||
|
||||
let _ = buf.write_i32::<BigEndian>(self.0.len() as i32);
|
||||
let _ = buf.write_i32::<BigEndian>(0); // index offset
|
||||
try!(w.write_be_i32(self.0.len() as i32));
|
||||
try!(w.write_be_i32(0)); // index offset
|
||||
|
||||
for e in self.0 {
|
||||
match try!(e.to_sql(&member_type)) {
|
||||
Some(inner_buf) => {
|
||||
let _ = buf.write_i32::<BigEndian>(inner_buf.len() as i32);
|
||||
let _ = buf.write_all(&inner_buf);
|
||||
}
|
||||
None => {
|
||||
let _ = buf.write_i32::<BigEndian>(-1);
|
||||
let mut inner_buf = vec![];
|
||||
match try!(e.to_sql(&member_type, &mut inner_buf)) {
|
||||
IsNull::No => {
|
||||
try!(w.write_be_i32(inner_buf.len() as i32));
|
||||
try!(w.write_all(&inner_buf));
|
||||
}
|
||||
IsNull::Yes => try!(w.write_be_i32(-1)),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(buf))
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
match ty.kind() {
|
||||
&Kind::Array(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use time::Timespec;
|
||||
use Result;
|
||||
use types::{Type, FromSql, RawToSql};
|
||||
use types::{Type, FromSql, ToSql, IsNull};
|
||||
|
||||
const USEC_PER_SEC: i64 = 1_000_000;
|
||||
const NSEC_PER_USEC: i64 = 1_000;
|
||||
@ -25,12 +25,13 @@ impl FromSql for Timespec {
|
||||
accepts!(Type::Timestamp, Type::TimestampTZ);
|
||||
}
|
||||
|
||||
impl RawToSql for Timespec {
|
||||
fn raw_to_sql<W: Writer>(&self, _: &Type, w: &mut W) -> Result<()> {
|
||||
impl ToSql for Timespec {
|
||||
fn to_sql<W: Writer+?Sized>(&self, _: &Type, w: &mut W) -> Result<IsNull> {
|
||||
let t = (self.sec - TIME_SEC_CONVERSION) * USEC_PER_SEC + self.nsec as i64 / NSEC_PER_USEC;
|
||||
Ok(try!(w.write_be_i64(t)))
|
||||
try!(w.write_be_i64(t));
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
accepts!(Type::Timestamp, Type::TimestampTZ);
|
||||
to_sql_checked!();
|
||||
}
|
||||
|
||||
to_raw_to_impl!(Type::Timestamp, Type::TimestampTZ; Timespec);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
extern crate uuid;
|
||||
|
||||
use self::uuid::Uuid;
|
||||
use types::{FromSql, ToSql, RawToSql, Type};
|
||||
use types::{FromSql, ToSql, Type, IsNull};
|
||||
use Error;
|
||||
use Result;
|
||||
|
||||
@ -16,10 +16,12 @@ impl FromSql for Uuid {
|
||||
accepts!(Type::Uuid);
|
||||
}
|
||||
|
||||
impl RawToSql for Uuid {
|
||||
fn raw_to_sql<W: Writer>(&self, _: &Type, w: &mut W) -> Result<()> {
|
||||
Ok(try!(w.write_all(self.as_bytes())))
|
||||
impl ToSql for Uuid {
|
||||
fn to_sql<W: Writer+?Sized>(&self, _: &Type, w: &mut W) -> Result<IsNull> {
|
||||
try!(w.write_all(self.as_bytes()));
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
}
|
||||
|
||||
to_raw_to_impl!(Type::Uuid; Uuid);
|
||||
accepts!(Type::Uuid);
|
||||
to_sql_checked!();
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ fn test_slice_wrong_type() {
|
||||
match stmt.query(&[&Slice(&["hi"])]) {
|
||||
Ok(_) => panic!("Unexpected success"),
|
||||
Err(Error::WrongType(..)) => {}
|
||||
Err(e) => panic!("Unexpected error {}", e),
|
||||
Err(e) => panic!("Unexpected error {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,6 +241,6 @@ fn test_slice_range() {
|
||||
match stmt.query(&[&Slice(&[1i64])]) {
|
||||
Ok(_) => panic!("Unexpected success"),
|
||||
Err(Error::WrongType(..)) => {}
|
||||
Err(e) => panic!("Unexpected error {}", e),
|
||||
Err(e) => panic!("Unexpected error {:?}", e),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user