use fallible_iterator::FallibleIterator;
use postgres_protocol;
use postgres_protocol::types::{self, ArrayDimension};
use std::borrow::Cow;
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::sync::Arc;
use types::type_gen::{Inner, Other};
#[doc(inline)]
pub use postgres_protocol::Oid;
pub use types::type_gen::consts::*;
pub use types::special::{Date, Timestamp};
#[macro_export]
macro_rules! accepts {
($($expected:pat),+) => (
fn accepts(ty: &$crate::types::Type) -> bool {
match *ty {
$($expected)|+ => true,
_ => false
}
}
)
}
#[macro_export]
macro_rules! to_sql_checked {
() => {
fn to_sql_checked(&self,
ty: &$crate::types::Type,
out: &mut ::std::vec::Vec<u8>)
-> ::std::result::Result<$crate::types::IsNull,
Box<::std::error::Error +
::std::marker::Sync +
::std::marker::Send>> {
$crate::types::__to_sql_checked(self, ty, out)
}
}
}
#[doc(hidden)]
pub fn __to_sql_checked<T>(
v: &T,
ty: &Type,
out: &mut Vec<u8>,
) -> Result<IsNull, Box<Error + Sync + Send>>
where
T: ToSql,
{
if !T::accepts(ty) {
return Err(Box::new(WrongType(ty.clone())));
}
v.to_sql(ty, out)
}
#[cfg(feature = "with-bit-vec")]
mod bit_vec;
#[cfg(feature = "with-uuid")]
mod uuid;
#[cfg(feature = "with-time")]
mod time;
#[cfg(feature = "with-rustc-serialize")]
mod rustc_serialize;
#[cfg(feature = "with-serde_json")]
mod serde_json;
#[cfg(feature = "with-chrono")]
mod chrono;
#[cfg(feature = "with-eui48")]
mod eui48;
#[cfg(feature = "with-geo")]
mod geo;
mod special;
mod type_gen;
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Type(Inner);
impl fmt::Display for Type {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self.schema() {
"public" | "pg_catalog" => {}
schema => write!(fmt, "{}.", schema)?,
}
fmt.write_str(self.name())
}
}
impl Type {
#[doc(hidden)]
pub fn _new(name: String, oid: Oid, kind: Kind, schema: String) -> Type {
Type(Inner::Other(Arc::new(Other {
name: name,
oid: oid,
kind: kind,
schema: schema,
})))
}
pub fn from_oid(oid: Oid) -> Option<Type> {
Inner::from_oid(oid).map(Type)
}
pub fn oid(&self) -> Oid {
self.0.oid()
}
pub fn kind(&self) -> &Kind {
self.0.kind()
}
pub fn schema(&self) -> &str {
match self.0 {
Inner::Other(ref u) => &u.schema,
_ => "pg_catalog",
}
}
pub fn name(&self) -> &str {
self.0.name()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Kind {
Simple,
Enum(Vec<String>),
Pseudo,
Array(Type),
Range(Type),
Domain(Type),
Composite(Vec<Field>),
#[doc(hidden)]
__PseudoPrivateForExtensibility,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Field {
name: String,
type_: Type,
}
impl Field {
pub fn name(&self) -> &str {
&self.name
}
pub fn type_(&self) -> &Type {
&self.type_
}
}
impl Field {
#[doc(hidden)]
pub fn new(name: String, type_: Type) -> Field {
Field {
name: name,
type_: type_,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct WasNull;
impl fmt::Display for WasNull {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(self.description())
}
}
impl Error for WasNull {
fn description(&self) -> &str {
"a Postgres value was `NULL`"
}
}
#[derive(Debug)]
pub struct WrongType(Type);
impl fmt::Display for WrongType {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(
fmt,
"cannot convert to or from a Postgres value of type `{}`",
self.0
)
}
}
impl Error for WrongType {
fn description(&self) -> &str {
"cannot convert to or from a Postgres value"
}
}
impl WrongType {
#[doc(hidden)]
pub fn new(ty: Type) -> WrongType {
WrongType(ty)
}
}
pub trait FromSql: Sized {
fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>>;
#[allow(unused_variables)]
fn from_sql_null(ty: &Type) -> Result<Self, Box<Error + Sync + Send>> {
Err(Box::new(WasNull))
}
fn from_sql_nullable(ty: &Type, raw: Option<&[u8]>) -> Result<Self, Box<Error + Sync + Send>> {
match raw {
Some(raw) => Self::from_sql(ty, raw),
None => Self::from_sql_null(ty),
}
}
fn accepts(ty: &Type) -> bool;
}
impl<T: FromSql> FromSql for Option<T> {
fn from_sql(ty: &Type, raw: &[u8]) -> Result<Option<T>, Box<Error + Sync + Send>> {
<T as FromSql>::from_sql(ty, raw).map(Some)
}
fn from_sql_null(_: &Type) -> Result<Option<T>, Box<Error + Sync + Send>> {
Ok(None)
}
fn accepts(ty: &Type) -> bool {
<T as FromSql>::accepts(ty)
}
}
impl<T: FromSql> FromSql for Vec<T> {
fn from_sql(ty: &Type, raw: &[u8]) -> Result<Vec<T>, Box<Error + Sync + Send>> {
let member_type = match *ty.kind() {
Kind::Array(ref member) => member,
_ => panic!("expected array type"),
};
let array = types::array_from_sql(raw)?;
if array.dimensions().count()? > 1 {
return Err("array contains too many dimensions".into());
}
array
.values()
.and_then(|v| T::from_sql_nullable(member_type, v))
.collect()
}
fn accepts(ty: &Type) -> bool {
match *ty.kind() {
Kind::Array(ref inner) => T::accepts(inner),
_ => false,
}
}
}
impl FromSql for Vec<u8> {
fn from_sql(_: &Type, raw: &[u8]) -> Result<Vec<u8>, Box<Error + Sync + Send>> {
Ok(types::bytea_from_sql(raw).to_owned())
}
accepts!(BYTEA);
}
impl FromSql for String {
fn from_sql(_: &Type, raw: &[u8]) -> Result<String, Box<Error + Sync + Send>> {
types::text_from_sql(raw).map(|b| b.to_owned())
}
fn accepts(ty: &Type) -> bool {
match *ty {
VARCHAR | TEXT | BPCHAR | NAME | UNKNOWN => true,
ref ty if ty.name() == "citext" => true,
_ => false,
}
}
}
macro_rules! simple_from {
($t:ty, $f:ident, $($expected:pat),+) => {
impl FromSql for $t {
fn from_sql(_: &Type,
raw: &[u8])
-> Result<$t, Box<Error + Sync + Send>> {
types::$f(raw)
}
accepts!($($expected),+);
}
}
}
simple_from!(bool, bool_from_sql, BOOL);
simple_from!(i8, char_from_sql, CHAR);
simple_from!(i16, int2_from_sql, INT2);
simple_from!(i32, int4_from_sql, INT4);
simple_from!(u32, oid_from_sql, OID);
simple_from!(i64, int8_from_sql, INT8);
simple_from!(f32, float4_from_sql, FLOAT4);
simple_from!(f64, float8_from_sql, FLOAT8);
impl FromSql for HashMap<String, Option<String>> {
fn from_sql(
_: &Type,
raw: &[u8],
) -> Result<HashMap<String, Option<String>>, Box<Error + Sync + Send>> {
types::hstore_from_sql(raw)?
.map(|(k, v)| (k.to_owned(), v.map(str::to_owned)))
.collect()
}
fn accepts(ty: &Type) -> bool {
ty.name() == "hstore"
}
}
pub enum IsNull {
Yes,
No,
}
pub trait ToSql: fmt::Debug {
fn to_sql(&self, ty: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>>
where
Self: Sized;
fn accepts(ty: &Type) -> bool
where
Self: Sized;
fn to_sql_checked(
&self,
ty: &Type,
out: &mut Vec<u8>,
) -> Result<IsNull, Box<Error + Sync + Send>>;
}
impl<'a, T> ToSql for &'a T
where
T: ToSql,
{
fn to_sql(&self, ty: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
(*self).to_sql(ty, out)
}
fn accepts(ty: &Type) -> bool {
T::accepts(ty)
}
to_sql_checked!();
}
impl<T: ToSql> ToSql for Option<T> {
fn to_sql(&self, ty: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
match *self {
Some(ref val) => val.to_sql(ty, out),
None => Ok(IsNull::Yes),
}
}
fn accepts(ty: &Type) -> bool {
<T as ToSql>::accepts(ty)
}
to_sql_checked!();
}
impl<'a, T: ToSql> ToSql for &'a [T] {
fn to_sql(&self, ty: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
let member_type = match *ty.kind() {
Kind::Array(ref member) => member,
_ => panic!("expected array type"),
};
let dimension = ArrayDimension {
len: downcast(self.len())?,
lower_bound: 1,
};
types::array_to_sql(
Some(dimension),
true,
member_type.oid(),
self.iter(),
|e, w| match e.to_sql(member_type, w)? {
IsNull::No => Ok(postgres_protocol::IsNull::No),
IsNull::Yes => Ok(postgres_protocol::IsNull::Yes),
},
w,
)?;
Ok(IsNull::No)
}
fn accepts(ty: &Type) -> bool {
match *ty.kind() {
Kind::Array(ref member) => T::accepts(member),
_ => false,
}
}
to_sql_checked!();
}
impl<'a> ToSql for &'a [u8] {
fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
types::bytea_to_sql(*self, w);
Ok(IsNull::No)
}
accepts!(BYTEA);
to_sql_checked!();
}
impl<T: ToSql> ToSql for Vec<T> {
fn to_sql(&self, ty: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
<&[T] as ToSql>::to_sql(&&**self, ty, w)
}
fn accepts(ty: &Type) -> bool {
<&[T] as ToSql>::accepts(ty)
}
to_sql_checked!();
}
impl ToSql for Vec<u8> {
fn to_sql(&self, ty: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
<&[u8] as ToSql>::to_sql(&&**self, ty, w)
}
fn accepts(ty: &Type) -> bool {
<&[u8] as ToSql>::accepts(ty)
}
to_sql_checked!();
}
impl<'a> ToSql for &'a str {
fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
types::text_to_sql(*self, w);
Ok(IsNull::No)
}
fn accepts(ty: &Type) -> bool {
match *ty {
VARCHAR | TEXT | BPCHAR | NAME | UNKNOWN => true,
ref ty if ty.name() == "citext" => true,
_ => false,
}
}
to_sql_checked!();
}
impl<'a> ToSql for Cow<'a, str> {
fn to_sql(&self, ty: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
<&str as ToSql>::to_sql(&&self.as_ref(), ty, w)
}
fn accepts(ty: &Type) -> bool {
<&str as ToSql>::accepts(ty)
}
to_sql_checked!();
}
impl ToSql for String {
fn to_sql(&self, ty: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
<&str as ToSql>::to_sql(&&**self, ty, w)
}
fn accepts(ty: &Type) -> bool {
<&str as ToSql>::accepts(ty)
}
to_sql_checked!();
}
macro_rules! simple_to {
($t:ty, $f:ident, $($expected:pat),+) => {
impl ToSql for $t {
fn to_sql(&self,
_: &Type,
w: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
types::$f(*self, w);
Ok(IsNull::No)
}
accepts!($($expected),+);
to_sql_checked!();
}
}
}
simple_to!(bool, bool_to_sql, BOOL);
simple_to!(i8, char_to_sql, CHAR);
simple_to!(i16, int2_to_sql, INT2);
simple_to!(i32, int4_to_sql, INT4);
simple_to!(u32, oid_to_sql, OID);
simple_to!(i64, int8_to_sql, INT8);
simple_to!(f32, float4_to_sql, FLOAT4);
simple_to!(f64, float8_to_sql, FLOAT8);
impl ToSql for HashMap<String, Option<String>> {
fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
types::hstore_to_sql(
self.iter().map(|(k, v)| (&**k, v.as_ref().map(|v| &**v))),
w,
)?;
Ok(IsNull::No)
}
fn accepts(ty: &Type) -> bool {
ty.name() == "hstore"
}
to_sql_checked!();
}
fn downcast(len: usize) -> Result<i32, Box<Error + Sync + Send>> {
if len > i32::max_value() as usize {
Err("value too large to transmit".into())
} else {
Ok(len as i32)
}
}