Always implement AsExpression for double references

The only time that a double reference `AsExpression` impl comes up is
from the derived impl of `Insertable` and `AsChangeset`. We can't vary
the generated code based on whether the type of a field is `Copy`, so we
have to assume that it isn't and always take a reference.

Previously we only did this for `str` and slices, as `String` and `Vec`
are the only non-copy types that we support (I think `BigDecimal` may
fall into that category now, but it's unlikely that avoiding a single
clone of that type is ever relevant).

Still, there's no reason that we shouldn't allow references of any type
to appear in `Insertable` structs. To avoid code bloat, I've cleaned
things up a bit and implemented more stuff on `str` and `[T]` directly,
letting the blanket reference impl apply to those types when it's
relevant.

Fixes #1242.
This commit is contained in:
Sean Griffin 2017-10-08 10:14:36 -06:00
parent aebc8723dc
commit dd169fc07c
7 changed files with 82 additions and 23 deletions

View File

@ -81,6 +81,9 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
* MySQL URLs will now properly percent decode the username and password.
* References to types other than `str` and slice can now appear on structs which
derive `Insertable` or `AsChangeset`.
## [0.16.0] - 2017-08-24
### Added

View File

@ -467,6 +467,40 @@ mod tests {
assert_eq!(Ok(expected), saved);
}
#[test]
fn named_struct_with_unusual_reference_type() {
struct NewUser<'a> {
name: &'a String,
hair_color: Option<&'a String>,
}
impl_Insertable! {
(users)
struct NewUser<'a> {
name: &'a String,
hair_color: Option<&'a String>,
}
}
let conn = connection();
let sean = "Sean".to_string();
let black = "Black".to_string();
let new_user = NewUser {
name: &sean,
hair_color: Some(&black),
};
::insert_into(users::table)
.values(&new_user)
.execute(&conn)
.unwrap();
let saved = users::table
.select((users::name, users::hair_color))
.load(&conn);
let expected = vec![(sean.clone(), Some(black.clone()))];
assert_eq!(Ok(expected), saved);
}
cfg_if! {
if #[cfg(feature = "sqlite")] {
fn connection() -> ::test_helpers::TestConnection {

View File

@ -111,8 +111,10 @@ array_as_expression!(Vec<T>, Array<ST>);
array_as_expression!(Vec<T>, Nullable<Array<ST>>);
array_as_expression!(&'a Vec<T>, Array<ST>);
array_as_expression!(&'a Vec<T>, Nullable<Array<ST>>);
array_as_expression!(&'a &'b Vec<T>, Array<ST>);
array_as_expression!(&'a &'b Vec<T>, Nullable<Array<ST>>);
impl<'a, ST, T> ToSql<Array<ST>, Pg> for &'a [T]
impl<ST, T> ToSql<Array<ST>, Pg> for [T]
where
Pg: HasSqlType<ST>,
T: ToSql<ST, Pg>,
@ -148,10 +150,10 @@ where
}
}
impl<'a, ST, T> ToSql<Nullable<Array<ST>>, Pg> for &'a [T]
impl<ST, T> ToSql<Nullable<Array<ST>>, Pg> for [T]
where
Pg: HasSqlType<ST>,
&'a [T]: ToSql<Array<ST>, Pg>,
[T]: ToSql<Array<ST>, Pg>,
{
fn to_sql<W: Write>(
&self,
@ -164,7 +166,7 @@ where
impl<ST, T> ToSql<Array<ST>, Pg> for Vec<T>
where
Pg: HasSqlType<ST>,
for<'a> &'a [T]: ToSql<Array<ST>, Pg>,
[T]: ToSql<Array<ST>, Pg>,
T: fmt::Debug,
{
fn to_sql<W: Write>(

View File

@ -31,11 +31,11 @@ queryable_impls!(Time -> String);
queryable_impls!(Timestamp -> String);
expression_impls!(Date -> String);
expression_impls!(Date -> &'a str);
expression_impls!(Date -> str, unsized);
expression_impls!(Time -> String);
expression_impls!(Time -> &'a str);
expression_impls!(Time -> str, unsized);
expression_impls!(Timestamp -> String);
expression_impls!(Timestamp -> &'a str);
expression_impls!(Timestamp -> str, unsized);
impl FromSql<types::Date, Sqlite> for String {
fn from_sql(value: Option<&SqliteValue>) -> Result<Self, Box<Error + Send + Sync>> {
@ -43,7 +43,7 @@ impl FromSql<types::Date, Sqlite> for String {
}
}
impl<'a> ToSql<types::Date, Sqlite> for &'a str {
impl ToSql<types::Date, Sqlite> for str {
fn to_sql<W: Write>(
&self,
out: &mut ToSqlOutput<W, Sqlite>,
@ -67,7 +67,7 @@ impl FromSql<types::Time, Sqlite> for String {
}
}
impl<'a> ToSql<types::Time, Sqlite> for &'a str {
impl ToSql<types::Time, Sqlite> for str {
fn to_sql<W: Write>(
&self,
out: &mut ToSqlOutput<W, Sqlite>,
@ -91,7 +91,7 @@ impl FromSql<types::Timestamp, Sqlite> for String {
}
}
impl<'a> ToSql<types::Timestamp, Sqlite> for &'a str {
impl ToSql<types::Timestamp, Sqlite> for str {
fn to_sql<W: Write>(
&self,
out: &mut ToSqlOutput<W, Sqlite>,

View File

@ -13,7 +13,9 @@ macro_rules! not_none {
#[macro_export]
macro_rules! expression_impls {
($Source:ident -> $Target:ty) => {
impl<'a> $crate::expression::AsExpression<$Source> for $Target {
expression_impls!($Source -> $Target, unsized);
impl $crate::expression::AsExpression<$Source> for $Target {
type Expression = $crate::expression::bound::Bound<$Source, Self>;
fn as_expression(self) -> Self::Expression {
@ -21,7 +23,17 @@ macro_rules! expression_impls {
}
}
impl<'a, 'expr> $crate::expression::AsExpression<$Source> for &'expr $Target {
impl $crate::expression::AsExpression<$crate::types::Nullable<$Source>> for $Target {
type Expression = $crate::expression::bound::Bound<$crate::types::Nullable<$Source>, Self>;
fn as_expression(self) -> Self::Expression {
$crate::expression::bound::Bound::new(self)
}
}
};
($Source:ident -> $Target:ty, unsized) => {
impl<'expr> $crate::expression::AsExpression<$Source> for &'expr $Target {
type Expression = $crate::expression::bound::Bound<$Source, Self>;
fn as_expression(self) -> Self::Expression {
@ -29,7 +41,15 @@ macro_rules! expression_impls {
}
}
impl<'a> $crate::expression::AsExpression<$crate::types::Nullable<$Source>> for $Target {
impl<'expr, 'expr2> $crate::expression::AsExpression<$Source> for &'expr2 &'expr $Target {
type Expression = $crate::expression::bound::Bound<$Source, Self>;
fn as_expression(self) -> Self::Expression {
$crate::expression::bound::Bound::new(self)
}
}
impl<'expr> $crate::expression::AsExpression<$crate::types::Nullable<$Source>> for &'expr $Target {
type Expression = $crate::expression::bound::Bound<$crate::types::Nullable<$Source>, Self>;
fn as_expression(self) -> Self::Expression {
@ -37,7 +57,7 @@ macro_rules! expression_impls {
}
}
impl<'a, 'expr> $crate::expression::AsExpression<$crate::types::Nullable<$Source>> for &'expr $Target {
impl<'expr, 'expr2> $crate::expression::AsExpression<$crate::types::Nullable<$Source>> for &'expr2 &'expr $Target {
type Expression = $crate::expression::bound::Bound<$crate::types::Nullable<$Source>, Self>;
fn as_expression(self) -> Self::Expression {
@ -45,7 +65,7 @@ macro_rules! expression_impls {
}
}
impl<'a, DB> $crate::types::ToSql<$crate::types::Nullable<$Source>, DB> for $Target where
impl<DB> $crate::types::ToSql<$crate::types::Nullable<$Source>, DB> for $Target where
DB: $crate::backend::Backend + $crate::types::HasSqlType<$Source>,
$Target: $crate::types::ToSql<$Source, DB>,
{
@ -53,7 +73,7 @@ macro_rules! expression_impls {
$crate::types::ToSql::<$Source, DB>::to_sql(self, out)
}
}
}
};
}
#[doc(hidden)]

View File

@ -22,8 +22,8 @@ primitive_impls!(Date);
primitive_impls!(Time);
primitive_impls!(Timestamp);
expression_impls!(Text -> &'a str);
expression_impls!(Binary -> &'a [u8]);
expression_impls!(Text -> str, unsized);
expression_impls!(Binary -> [u8], unsized);
impl NotNull for () {}
@ -34,7 +34,7 @@ impl<DB: Backend<RawValue = [u8]>> FromSql<types::Text, DB> for String {
}
}
impl<'a, DB: Backend> ToSql<types::Text, DB> for &'a str {
impl<DB: Backend> ToSql<types::Text, DB> for str {
fn to_sql<W: Write>(
&self,
out: &mut ToSqlOutput<W, DB>,
@ -48,7 +48,7 @@ impl<'a, DB: Backend> ToSql<types::Text, DB> for &'a str {
impl<DB> ToSql<types::Text, DB> for String
where
DB: Backend,
for<'a> &'a str: ToSql<types::Text, DB>,
str: ToSql<types::Text, DB>,
{
fn to_sql<W: Write>(
&self,
@ -67,7 +67,7 @@ impl<DB: Backend<RawValue = [u8]>> FromSql<types::Binary, DB> for Vec<u8> {
impl<DB> ToSql<types::Binary, DB> for Vec<u8>
where
DB: Backend,
for<'a> &'a [u8]: ToSql<types::Binary, DB>,
[u8]: ToSql<types::Binary, DB>,
{
fn to_sql<W: Write>(
&self,
@ -77,7 +77,7 @@ where
}
}
impl<'a, DB: Backend> ToSql<types::Binary, DB> for &'a [u8] {
impl<DB: Backend> ToSql<types::Binary, DB> for [u8] {
fn to_sql<W: Write>(
&self,
out: &mut ToSqlOutput<W, DB>,

View File

@ -501,7 +501,7 @@ pub trait ToSql<A, DB: Backend + HasSqlType<A>>: fmt::Debug {
impl<'a, A, T, DB> ToSql<A, DB> for &'a T
where
DB: Backend + HasSqlType<A>,
T: ToSql<A, DB>,
T: ToSql<A, DB> + ?Sized,
{
fn to_sql<W: Write>(
&self,