fix(any): rework decode and encode to not use blanket impls

This commit is contained in:
Ryan Leckey 2020-06-27 18:53:32 -07:00
parent e2dabeeeee
commit f9d961ae1d
5 changed files with 361 additions and 273 deletions

View File

@ -1,7 +1,4 @@
use crate::any::value::AnyValueRefKind;
use crate::any::{Any, AnyValueRef};
use crate::decode::Decode;
use crate::error::BoxDynError;
use crate::types::Type;
#[cfg(feature = "postgres")]
@ -18,29 +15,39 @@ use crate::sqlite::Sqlite;
// Implements Decode for any T where T supports Decode for any database that has support currently
// compiled into SQLx
impl<'r, T> Decode<'r, Any> for T
where
T: AnyDecode<'r>,
{
fn decode(value: AnyValueRef<'r>) -> Result<Self, BoxDynError> {
macro_rules! impl_any_decode {
($ty:ty) => {
impl<'r> crate::decode::Decode<'r, crate::any::Any> for $ty
where
$ty: crate::any::AnyDecode<'r>,
{
fn decode(
value: crate::any::AnyValueRef<'r>,
) -> Result<Self, crate::error::BoxDynError> {
match value.0 {
#[cfg(feature = "mysql")]
AnyValueRefKind::MySql(value) => <T as Decode<'r, crate::mysql::MySql>>::decode(value),
crate::any::value::AnyValueRefKind::MySql(value) => {
<$ty as crate::decode::Decode<'r, crate::mysql::MySql>>::decode(value)
}
#[cfg(feature = "sqlite")]
AnyValueRefKind::Sqlite(value) => {
<T as Decode<'r, crate::sqlite::Sqlite>>::decode(value)
crate::any::value::AnyValueRefKind::Sqlite(value) => {
<$ty as crate::decode::Decode<'r, crate::sqlite::Sqlite>>::decode(value)
}
#[cfg(feature = "mssql")]
AnyValueRefKind::Mssql(value) => <T as Decode<'r, crate::mssql::Mssql>>::decode(value),
crate::any::value::AnyValueRefKind::Mssql(value) => {
<$ty as crate::decode::Decode<'r, crate::mssql::Mssql>>::decode(value)
}
#[cfg(feature = "postgres")]
AnyValueRefKind::Postgres(value) => {
<T as Decode<'r, crate::postgres::Postgres>>::decode(value)
crate::any::value::AnyValueRefKind::Postgres(value) => {
<$ty as crate::decode::Decode<'r, crate::postgres::Postgres>>::decode(value)
}
}
}
}
};
}
// FIXME: Find a nice way to auto-generate the below or petition Rust to add support for #[cfg]

View File

@ -1,6 +1,4 @@
use crate::any::arguments::AnyArgumentBufferKind;
use crate::any::{Any, AnyArgumentBuffer};
use crate::encode::{Encode, IsNull};
use crate::encode::Encode;
use crate::types::Type;
#[cfg(feature = "postgres")]
@ -17,28 +15,37 @@ use crate::sqlite::Sqlite;
// Implements Encode for any T where T supports Encode for any database that has support currently
// compiled into SQLx
impl<'q, T> Encode<'q, Any> for T
where
T: AnyEncode<'q>,
{
fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer<'q>) -> IsNull {
macro_rules! impl_any_encode {
($ty:ty) => {
impl<'q> crate::encode::Encode<'q, crate::any::Any> for $ty
where
$ty: crate::any::AnyEncode<'q>,
{
fn encode_by_ref(
&self,
buf: &mut crate::any::AnyArgumentBuffer<'q>,
) -> crate::encode::IsNull {
match &mut buf.0 {
#[cfg(feature = "postgres")]
AnyArgumentBufferKind::Postgres(args, _) => args.add(self),
crate::any::arguments::AnyArgumentBufferKind::Postgres(args, _) => {
args.add(self)
}
#[cfg(feature = "mysql")]
AnyArgumentBufferKind::MySql(args, _) => args.add(self),
crate::any::arguments::AnyArgumentBufferKind::MySql(args, _) => args.add(self),
#[cfg(feature = "mssql")]
AnyArgumentBufferKind::Mssql(args, _) => args.add(self),
crate::any::arguments::AnyArgumentBufferKind::Mssql(args, _) => args.add(self),
#[cfg(feature = "sqlite")]
AnyArgumentBufferKind::Sqlite(args) => args.add(self),
crate::any::arguments::AnyArgumentBufferKind::Sqlite(args) => args.add(self),
}
// unused
IsNull::No
crate::encode::IsNull::No
}
}
};
}
// FIXME: Find a nice way to auto-generate the below or petition Rust to add support for #[cfg]

View File

@ -1,13 +1,20 @@
#[macro_use]
mod decode;
#[macro_use]
mod encode;
#[macro_use]
mod r#type;
mod arguments;
mod connection;
mod database;
mod decode;
mod encode;
mod options;
mod row;
mod transaction;
mod type_info;
mod types;
pub mod types;
mod value;
pub use arguments::{AnyArgumentBuffer, AnyArguments};
@ -16,6 +23,7 @@ pub use database::Any;
pub use decode::AnyDecode;
pub use encode::AnyEncode;
pub use options::AnyConnectOptions;
pub use r#type::AnyType;
pub use row::AnyRow;
pub use transaction::AnyTransactionManager;
pub use type_info::AnyTypeInfo;

252
sqlx-core/src/any/type.rs Normal file
View File

@ -0,0 +1,252 @@
use crate::types::Type;
#[cfg(feature = "postgres")]
use crate::postgres::Postgres;
#[cfg(feature = "mysql")]
use crate::mysql::MySql;
#[cfg(feature = "mssql")]
use crate::mssql::Mssql;
#[cfg(feature = "sqlite")]
use crate::sqlite::Sqlite;
// Type is required by the bounds of the [Row] and [Arguments] trait but its been overridden in
// AnyRow and AnyArguments to not use this implementation; but instead, delegate to the
// database-specific implementation.
//
// The other use of this trait is for compile-time verification which is not feasible to support
// for the [Any] driver.
macro_rules! impl_any_type {
($ty:ty) => {
impl crate::types::Type<crate::any::Any> for $ty
where
$ty: crate::any::AnyType,
{
fn type_info() -> crate::any::AnyTypeInfo {
// FIXME: nicer panic explaining why this isn't possible
unimplemented!()
}
fn compatible(ty: &crate::any::AnyTypeInfo) -> bool {
match &ty.0 {
#[cfg(feature = "postgres")]
crate::any::type_info::AnyTypeInfoKind::Postgres(ty) => {
<$ty as crate::types::Type<crate::postgres::Postgres>>::compatible(&ty)
}
#[cfg(feature = "mysql")]
crate::any::type_info::AnyTypeInfoKind::MySql(ty) => {
<$ty as crate::types::Type<crate::mysql::MySql>>::compatible(&ty)
}
#[cfg(feature = "sqlite")]
crate::any::type_info::AnyTypeInfoKind::Sqlite(ty) => {
<$ty as crate::types::Type<crate::sqlite::Sqlite>>::compatible(&ty)
}
#[cfg(feature = "mssql")]
crate::any::type_info::AnyTypeInfoKind::Mssql(ty) => {
<$ty as crate::types::Type<crate::mssql::Mssql>>::compatible(&ty)
}
}
}
}
};
}
// FIXME: Find a nice way to auto-generate the below or petition Rust to add support for #[cfg]
// to trait bounds
// all 4
#[cfg(all(
feature = "postgres",
feature = "mysql",
feature = "mssql",
feature = "sqlite"
))]
pub trait AnyType: Type<Postgres> + Type<MySql> + Type<Mssql> + Type<Sqlite> {}
#[cfg(all(
feature = "postgres",
feature = "mysql",
feature = "mssql",
feature = "sqlite"
))]
impl<T: ?Sized> AnyType for T where T: Type<Postgres> + Type<MySql> + Type<Mssql> + Type<Sqlite> {}
// only 3 (4)
#[cfg(all(
not(feature = "mssql"),
all(feature = "postgres", feature = "mysql", feature = "sqlite")
))]
pub trait AnyType: Type<Postgres> + Type<MySql> + Type<Sqlite> {}
#[cfg(all(
not(feature = "mssql"),
all(feature = "postgres", feature = "mysql", feature = "sqlite")
))]
impl<T: ?Sized> AnyType for T where T: Type<Postgres> + Type<MySql> + Type<Sqlite> {}
#[cfg(all(
not(feature = "mysql"),
all(feature = "postgres", feature = "mssql", feature = "sqlite")
))]
pub trait AnyType: Type<Postgres> + Type<Mssql> + Type<Sqlite> {}
#[cfg(all(
not(feature = "mysql"),
all(feature = "postgres", feature = "mssql", feature = "sqlite")
))]
impl<T: ?Sized> AnyType for T where T: Type<Postgres> + Type<Mssql> + Type<Sqlite> {}
#[cfg(all(
not(feature = "sqlite"),
all(feature = "postgres", feature = "mysql", feature = "mssql")
))]
pub trait AnyType: Type<Postgres> + Type<MySql> + Type<Mssql> {}
#[cfg(all(
not(feature = "sqlite"),
all(feature = "postgres", feature = "mysql", feature = "mssql")
))]
impl<T: ?Sized> AnyType for T where T: Type<Postgres> + Type<MySql> + Type<Mssql> {}
#[cfg(all(
not(feature = "postgres"),
all(feature = "sqlite", feature = "mysql", feature = "mssql")
))]
pub trait AnyType: Type<Sqlite> + Type<MySql> + Type<Mssql> {}
#[cfg(all(
not(feature = "postgres"),
all(feature = "sqlite", feature = "mysql", feature = "mssql")
))]
impl<T: ?Sized> AnyType for T where T: Type<Sqlite> + Type<MySql> + Type<Mssql> {}
// only 2 (6)
#[cfg(all(
not(any(feature = "mssql", feature = "sqlite")),
all(feature = "postgres", feature = "mysql")
))]
pub trait AnyType: Type<Postgres> + Type<MySql> {}
#[cfg(all(
not(any(feature = "mssql", feature = "sqlite")),
all(feature = "postgres", feature = "mysql")
))]
impl<T: ?Sized> AnyType for T where T: Type<Postgres> + Type<MySql> {}
#[cfg(all(
not(any(feature = "mysql", feature = "sqlite")),
all(feature = "postgres", feature = "mssql")
))]
pub trait AnyType: Type<Postgres> + Type<Mssql> {}
#[cfg(all(
not(any(feature = "mysql", feature = "sqlite")),
all(feature = "postgres", feature = "mssql")
))]
impl<T: ?Sized> AnyType for T where T: Type<Postgres> + Type<Mssql> {}
#[cfg(all(
not(any(feature = "mysql", feature = "mssql")),
all(feature = "postgres", feature = "sqlite")
))]
pub trait AnyType: Type<Postgres> + Type<Sqlite> {}
#[cfg(all(
not(any(feature = "mysql", feature = "mssql")),
all(feature = "postgres", feature = "sqlite")
))]
impl<T: ?Sized> AnyType for T where T: Type<Postgres> + Type<Sqlite> {}
#[cfg(all(
not(any(feature = "postgres", feature = "sqlite")),
all(feature = "mssql", feature = "mysql")
))]
pub trait AnyType: Type<Mssql> + Type<MySql> {}
#[cfg(all(
not(any(feature = "postgres", feature = "sqlite")),
all(feature = "mssql", feature = "mysql")
))]
impl<T: ?Sized> AnyType for T where T: Type<Mssql> + Type<MySql> {}
#[cfg(all(
not(any(feature = "postgres", feature = "mysql")),
all(feature = "mssql", feature = "sqlite")
))]
pub trait AnyType: Type<Mssql> + Type<Sqlite> {}
#[cfg(all(
not(any(feature = "postgres", feature = "mysql")),
all(feature = "mssql", feature = "sqlite")
))]
impl<T: ?Sized> AnyType for T where T: Type<Mssql> + Type<Sqlite> {}
#[cfg(all(
not(any(feature = "postgres", feature = "mssql")),
all(feature = "mysql", feature = "sqlite")
))]
pub trait AnyType: Type<MySql> + Type<Sqlite> {}
#[cfg(all(
not(any(feature = "postgres", feature = "mssql")),
all(feature = "mysql", feature = "sqlite")
))]
impl<T: ?Sized> AnyType for T where T: Type<MySql> + Type<Sqlite> {}
// only 1 (4)
#[cfg(all(
not(any(feature = "mysql", feature = "mssql", feature = "sqlite")),
feature = "postgres"
))]
pub trait AnyType: Type<Postgres> {}
#[cfg(all(
not(any(feature = "mysql", feature = "mssql", feature = "sqlite")),
feature = "postgres"
))]
impl<T: ?Sized> AnyType for T where T: Type<Postgres> {}
#[cfg(all(
not(any(feature = "postgres", feature = "mssql", feature = "sqlite")),
feature = "mysql"
))]
pub trait AnyType: Type<MySql> {}
#[cfg(all(
not(any(feature = "postgres", feature = "mssql", feature = "sqlite")),
feature = "mysql"
))]
impl<T: ?Sized> AnyType for T where T: Type<MySql> {}
#[cfg(all(
not(any(feature = "mysql", feature = "postgres", feature = "sqlite")),
feature = "mssql"
))]
pub trait AnyType: Type<Mssql> {}
#[cfg(all(
not(any(feature = "mysql", feature = "postgres", feature = "sqlite")),
feature = "mssql"
))]
impl<T: ?Sized> AnyType for T where T: Type<Mssql> {}
#[cfg(all(
not(any(feature = "mysql", feature = "mssql", feature = "postgres")),
feature = "sqlite"
))]
pub trait AnyType: Type<Sqlite> {}
#[cfg(all(
not(any(feature = "mysql", feature = "mssql", feature = "postgres")),
feature = "sqlite"
))]
impl<T: ?Sized> AnyType for T where T: Type<Sqlite> {}

View File

@ -1,243 +1,57 @@
use crate::any::type_info::AnyTypeInfoKind;
use crate::any::{Any, AnyTypeInfo};
use crate::database::Database;
use crate::types::Type;
//! Conversions between Rust and standard **SQL** types.
//!
//! # Types
//!
//! | Rust type | SQL type(s) |
//! |---------------------------------------|------------------------------------------------------|
//! | `bool` | BOOLEAN |
//! | `i32` | INT |
//! | `i64` | BIGINT |
//! | `f32` | FLOAT |
//! | `f64` | DOUBLE |
//! | `&str`, `String` | VARCHAR, CHAR, TEXT |
//!
//! # Nullable
//!
//! In addition, `Option<T>` is supported where `T` implements `Type`. An `Option<T>` represents
//! a potentially `NULL` value from SQL.
//!
#[cfg(feature = "postgres")]
use crate::postgres::Postgres;
// Type
#[cfg(feature = "mysql")]
use crate::mysql::MySql;
impl_any_type!(bool);
#[cfg(feature = "mssql")]
use crate::mssql::Mssql;
impl_any_type!(i32);
impl_any_type!(i64);
#[cfg(feature = "sqlite")]
use crate::sqlite::Sqlite;
impl_any_type!(f32);
impl_any_type!(f64);
// Type is required by the bounds of the [Row] and [Arguments] trait but its been overridden in
// AnyRow and AnyArguments to not use this implementation; but instead, delegate to the
// database-specific implementation.
//
// The other use of this trait is for compile-time verification which is not feasible to support
// for the [Any] driver.
impl<T> Type<Any> for T
where
T: AnyType,
{
fn type_info() -> <Any as Database>::TypeInfo {
// FIXME: nicer panic explaining why this isn't possible
unimplemented!()
}
impl_any_type!(str);
impl_any_type!(String);
fn compatible(ty: &AnyTypeInfo) -> bool {
match &ty.0 {
#[cfg(feature = "postgres")]
AnyTypeInfoKind::Postgres(ty) => <T as Type<Postgres>>::compatible(&ty),
// Encode
#[cfg(feature = "mysql")]
AnyTypeInfoKind::MySql(ty) => <T as Type<MySql>>::compatible(&ty),
impl_any_encode!(bool);
#[cfg(feature = "sqlite")]
AnyTypeInfoKind::Sqlite(ty) => <T as Type<Sqlite>>::compatible(&ty),
impl_any_encode!(i32);
impl_any_encode!(i64);
#[cfg(feature = "mssql")]
AnyTypeInfoKind::Mssql(ty) => <T as Type<Mssql>>::compatible(&ty),
}
}
}
impl_any_encode!(f32);
impl_any_encode!(f64);
// FIXME: Find a nice way to auto-generate the below or petition Rust to add support for #[cfg]
// to trait bounds
impl_any_encode!(&'q str);
impl_any_encode!(String);
// all 4
// Decode
#[cfg(all(
feature = "postgres",
feature = "mysql",
feature = "mssql",
feature = "sqlite"
))]
pub trait AnyType: Type<Postgres> + Type<MySql> + Type<Mssql> + Type<Sqlite> {}
impl_any_decode!(bool);
#[cfg(all(
feature = "postgres",
feature = "mysql",
feature = "mssql",
feature = "sqlite"
))]
impl<T> AnyType for T where T: Type<Postgres> + Type<MySql> + Type<Mssql> + Type<Sqlite> {}
impl_any_decode!(i32);
impl_any_decode!(i64);
// only 3 (4)
impl_any_decode!(f32);
impl_any_decode!(f64);
#[cfg(all(
not(feature = "mssql"),
all(feature = "postgres", feature = "mysql", feature = "sqlite")
))]
pub trait AnyType: Type<Postgres> + Type<MySql> + Type<Sqlite> {}
#[cfg(all(
not(feature = "mssql"),
all(feature = "postgres", feature = "mysql", feature = "sqlite")
))]
impl<T> AnyType for T where T: Type<Postgres> + Type<MySql> + Type<Sqlite> {}
#[cfg(all(
not(feature = "mysql"),
all(feature = "postgres", feature = "mssql", feature = "sqlite")
))]
pub trait AnyType: Type<Postgres> + Type<Mssql> + Type<Sqlite> {}
#[cfg(all(
not(feature = "mysql"),
all(feature = "postgres", feature = "mssql", feature = "sqlite")
))]
impl<T> AnyType for T where T: Type<Postgres> + Type<Mssql> + Type<Sqlite> {}
#[cfg(all(
not(feature = "sqlite"),
all(feature = "postgres", feature = "mysql", feature = "mssql")
))]
pub trait AnyType: Type<Postgres> + Type<MySql> + Type<Mssql> {}
#[cfg(all(
not(feature = "sqlite"),
all(feature = "postgres", feature = "mysql", feature = "mssql")
))]
impl<T> AnyType for T where T: Type<Postgres> + Type<MySql> + Type<Mssql> {}
#[cfg(all(
not(feature = "postgres"),
all(feature = "sqlite", feature = "mysql", feature = "mssql")
))]
pub trait AnyType: Type<Sqlite> + Type<MySql> + Type<Mssql> {}
#[cfg(all(
not(feature = "postgres"),
all(feature = "sqlite", feature = "mysql", feature = "mssql")
))]
impl<T> AnyType for T where T: Type<Sqlite> + Type<MySql> + Type<Mssql> {}
// only 2 (6)
#[cfg(all(
not(any(feature = "mssql", feature = "sqlite")),
all(feature = "postgres", feature = "mysql")
))]
pub trait AnyType: Type<Postgres> + Type<MySql> {}
#[cfg(all(
not(any(feature = "mssql", feature = "sqlite")),
all(feature = "postgres", feature = "mysql")
))]
impl<T> AnyType for T where T: Type<Postgres> + Type<MySql> {}
#[cfg(all(
not(any(feature = "mysql", feature = "sqlite")),
all(feature = "postgres", feature = "mssql")
))]
pub trait AnyType: Type<Postgres> + Type<Mssql> {}
#[cfg(all(
not(any(feature = "mysql", feature = "sqlite")),
all(feature = "postgres", feature = "mssql")
))]
impl<T> AnyType for T where T: Type<Postgres> + Type<Mssql> {}
#[cfg(all(
not(any(feature = "mysql", feature = "mssql")),
all(feature = "postgres", feature = "sqlite")
))]
pub trait AnyType: Type<Postgres> + Type<Sqlite> {}
#[cfg(all(
not(any(feature = "mysql", feature = "mssql")),
all(feature = "postgres", feature = "sqlite")
))]
impl<T> AnyType for T where T: Type<Postgres> + Type<Sqlite> {}
#[cfg(all(
not(any(feature = "postgres", feature = "sqlite")),
all(feature = "mssql", feature = "mysql")
))]
pub trait AnyType: Type<Mssql> + Type<MySql> {}
#[cfg(all(
not(any(feature = "postgres", feature = "sqlite")),
all(feature = "mssql", feature = "mysql")
))]
impl<T> AnyType for T where T: Type<Mssql> + Type<MySql> {}
#[cfg(all(
not(any(feature = "postgres", feature = "mysql")),
all(feature = "mssql", feature = "sqlite")
))]
pub trait AnyType: Type<Mssql> + Type<Sqlite> {}
#[cfg(all(
not(any(feature = "postgres", feature = "mysql")),
all(feature = "mssql", feature = "sqlite")
))]
impl<T> AnyType for T where T: Type<Mssql> + Type<Sqlite> {}
#[cfg(all(
not(any(feature = "postgres", feature = "mssql")),
all(feature = "mysql", feature = "sqlite")
))]
pub trait AnyType: Type<MySql> + Type<Sqlite> {}
#[cfg(all(
not(any(feature = "postgres", feature = "mssql")),
all(feature = "mysql", feature = "sqlite")
))]
impl<T> AnyType for T where T: Type<MySql> + Type<Sqlite> {}
// only 1 (4)
#[cfg(all(
not(any(feature = "mysql", feature = "mssql", feature = "sqlite")),
feature = "postgres"
))]
pub trait AnyType: Type<Postgres> {}
#[cfg(all(
not(any(feature = "mysql", feature = "mssql", feature = "sqlite")),
feature = "postgres"
))]
impl<T> AnyType for T where T: Type<Postgres> {}
#[cfg(all(
not(any(feature = "postgres", feature = "mssql", feature = "sqlite")),
feature = "mysql"
))]
pub trait AnyType: Type<MySql> {}
#[cfg(all(
not(any(feature = "postgres", feature = "mssql", feature = "sqlite")),
feature = "mysql"
))]
impl<T> AnyType for T where T: Type<MySql> {}
#[cfg(all(
not(any(feature = "mysql", feature = "postgres", feature = "sqlite")),
feature = "mssql"
))]
pub trait AnyType: Type<Mssql> {}
#[cfg(all(
not(any(feature = "mysql", feature = "postgres", feature = "sqlite")),
feature = "mssql"
))]
impl<T> AnyType for T where T: Type<Mssql> {}
#[cfg(all(
not(any(feature = "mysql", feature = "mssql", feature = "postgres")),
feature = "sqlite"
))]
pub trait AnyType: Type<Sqlite> {}
#[cfg(all(
not(any(feature = "mysql", feature = "mssql", feature = "postgres")),
feature = "sqlite"
))]
impl<T> AnyType for T where T: Type<Sqlite> {}
impl_any_decode!(&'r str);
impl_any_decode!(String);