Allow operators to be used in more places

This implements `std::ops::{Add, Sub, Mul, Div}` for every type I could
think of. I've done this by introducing a new derive that we can use for
any type with a `where` clause on its `Expression` impl. I've left it
undocumented for now, because I think we may just want to bake this into
a different derive once RFC 2056 is stable.

I've also cleaned up the SQL type implementations, and added new ones
for `Interval`.

Fixes #1639.
This commit is contained in:
Sean Griffin 2018-05-04 11:39:07 -06:00
parent c44b2db5fb
commit b2edcdb02a
13 changed files with 180 additions and 89 deletions

View File

@ -23,6 +23,11 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
* Added `sqlite-bundled` feature to `diesel_cli` to make installing on
some platforms easier.
* All functions and operators provided by Diesel can now be used with numeric
operators if the SQL type supports it.
* `PgInterval` can now be used with `-`, `*`, and `/`.
### Changed
* `sql_function!` has been redesigned. The syntax is now `sql_function!(fn

View File

@ -4,10 +4,10 @@ use backend::Backend;
use query_builder::*;
use result::QueryResult;
use serialize::ToSql;
use super::*;
use sql_types::HasSqlType;
use super::*;
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, DieselNumericOps)]
pub struct Bound<T, U> {
item: U,
_marker: PhantomData<T>,

View File

@ -5,7 +5,7 @@ use expression::*;
use query_builder::*;
use result::QueryResult;
#[derive(Debug, Copy, Clone, QueryId)]
#[derive(Debug, Copy, Clone, QueryId, DieselNumericOps)]
#[doc(hidden)]
/// Coerces an expression to be another type. No checks are performed to ensure
/// that the new type is valid in all positions that the previous type was.

View File

@ -55,7 +55,7 @@ pub fn count_star() -> CountStar {
CountStar
}
#[derive(Debug, Clone, Copy, QueryId)]
#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps)]
#[doc(hidden)]
pub struct CountStar;

View File

@ -192,7 +192,7 @@ macro_rules! __diesel_sql_function_body {
use super::*;
use $crate::sql_types::*;
#[derive(Debug, Clone, Copy, QueryId)]
#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps)]
pub struct $fn_name<$($type_args,)* $($arg_name),*> {
$(pub(in super) $arg_name: $arg_name,)*
$(pub(in super) $type_args: ::std::marker::PhantomData<$type_args>,)*
@ -209,7 +209,7 @@ macro_rules! __diesel_sql_function_body {
$crate::expression::Expression
for $fn_name<$($type_args,)* $($arg_name),*>
where
for <'a> ($(&'a $arg_name),*): $crate::expression::Expression,
($($arg_name),*): $crate::expression::Expression,
{
type SqlType = $return_type;
}

View File

@ -3,7 +3,7 @@ use expression::{Expression, NonAggregate};
use query_builder::*;
use result::QueryResult;
#[derive(Debug, Copy, Clone, QueryId, Default)]
#[derive(Debug, Copy, Clone, QueryId, Default, DieselNumericOps)]
pub struct Grouped<T>(pub T);
impl<T: Expression> Expression for Grouped<T> {

View File

@ -4,7 +4,7 @@ use query_builder::*;
use result::QueryResult;
use sql_types::IntoNullable;
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, DieselNumericOps)]
pub struct Nullable<T>(T);
impl<T> Nullable<T> {

View File

@ -61,7 +61,7 @@ macro_rules! __diesel_operator_body {
expression_ty_params = ($($expression_ty_params:ident,)*),
expression_bounds = ($($expression_bounds:tt)*),
) => {
#[derive(Debug, Clone, Copy, QueryId)]
#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps)]
#[doc(hidden)]
pub struct $name<$($ty_param,)+> {
$(pub(crate) $field_name: $ty_param,)+

View File

@ -6,7 +6,7 @@ use query_builder::*;
use query_dsl::RunQueryDsl;
use result::QueryResult;
#[derive(Debug, Clone)]
#[derive(Debug, Clone, DieselNumericOps)]
#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."]
/// Returned by the [`sql()`] function.
///

View File

@ -492,25 +492,15 @@ pub mod sql_types {
mod ops {
use super::sql_types::*;
use sql_types::ops::*;
use sql_types::{Interval, Nullable};
use sql_types::Interval;
impl Add for Timestamptz {
type Rhs = Interval;
type Output = Timestamptz;
}
impl Add for Nullable<Timestamptz> {
type Rhs = Nullable<Interval>;
type Output = Nullable<Timestamptz>;
}
impl Sub for Timestamptz {
type Rhs = Interval;
type Output = Timestamptz;
}
impl Sub for Nullable<Timestamptz> {
type Rhs = Nullable<Interval>;
type Output = Nullable<Timestamptz>;
}
}

View File

@ -31,6 +31,8 @@
//! satisfied all constraints, Rust would not know which one to use, and there
//! would be no way for the user to specify which one should be used.
use super::*;
/// Represents SQL types which can be added.
pub trait Add {
/// The SQL type which can be added to this one
@ -66,44 +68,24 @@ pub trait Div {
macro_rules! numeric_type {
($($tpe: ident),*) => {
$(
impl Add for super::$tpe {
type Rhs = super::$tpe;
type Output = super::$tpe;
impl Add for $tpe {
type Rhs = $tpe;
type Output = $tpe;
}
impl Add for super::Nullable<super::$tpe> {
type Rhs = super::Nullable<super::$tpe>;
type Output = super::Nullable<super::$tpe>;
impl Sub for $tpe {
type Rhs = $tpe;
type Output = $tpe;
}
impl Sub for super::$tpe {
type Rhs = super::$tpe;
type Output = super::$tpe;
impl Mul for $tpe {
type Rhs = $tpe;
type Output = $tpe;
}
impl Sub for super::Nullable<super::$tpe> {
type Rhs = super::Nullable<super::$tpe>;
type Output = super::Nullable<super::$tpe>;
}
impl Mul for super::$tpe {
type Rhs = super::$tpe;
type Output = super::$tpe;
}
impl Mul for super::Nullable<super::$tpe> {
type Rhs = super::Nullable<super::$tpe>;
type Output = super::Nullable<super::$tpe>;
}
impl Div for super::$tpe {
type Rhs = super::$tpe;
type Output = super::$tpe;
}
impl Div for super::Nullable<super::$tpe> {
type Rhs = super::Nullable<super::$tpe>;
type Output = super::Nullable<super::$tpe>;
impl Div for $tpe {
type Rhs = $tpe;
type Output = $tpe;
}
)*
}
@ -111,62 +93,92 @@ macro_rules! numeric_type {
numeric_type!(SmallInt, Integer, BigInt, Float, Double, Numeric);
impl Add for super::Time {
type Rhs = super::Interval;
type Output = super::Time;
impl Add for Time {
type Rhs = Interval;
type Output = Time;
}
impl Add for super::Nullable<super::Time> {
type Rhs = super::Nullable<super::Interval>;
type Output = super::Nullable<super::Time>;
impl Sub for Time {
type Rhs = Interval;
type Output = Time;
}
impl Sub for super::Time {
type Rhs = super::Interval;
type Output = super::Time;
impl Add for Date {
type Rhs = Interval;
type Output = Timestamp;
}
impl Sub for super::Nullable<super::Time> {
type Rhs = super::Nullable<super::Interval>;
type Output = super::Nullable<super::Time>;
impl Sub for Date {
type Rhs = Interval;
type Output = Timestamp;
}
impl Add for super::Date {
type Rhs = super::Interval;
type Output = super::Timestamp;
impl Add for Timestamp {
type Rhs = Interval;
type Output = Timestamp;
}
impl Add for super::Nullable<super::Date> {
type Rhs = super::Nullable<super::Interval>;
type Output = super::Nullable<super::Timestamp>;
impl Sub for Timestamp {
type Rhs = Interval;
type Output = Timestamp;
}
impl Sub for super::Date {
type Rhs = super::Interval;
type Output = super::Timestamp;
impl Add for Interval {
type Rhs = Interval;
type Output = Interval;
}
impl Sub for super::Nullable<super::Date> {
type Rhs = super::Nullable<super::Interval>;
type Output = super::Nullable<super::Timestamp>;
impl Sub for Interval {
type Rhs = Interval;
type Output = Interval;
}
impl Add for super::Timestamp {
type Rhs = super::Interval;
type Output = super::Timestamp;
impl Mul for Interval {
type Rhs = Integer;
type Output = Interval;
}
impl Add for super::Nullable<super::Timestamp> {
type Rhs = super::Nullable<super::Interval>;
type Output = super::Nullable<super::Timestamp>;
impl Div for Interval {
type Rhs = Integer;
type Output = Interval;
}
impl Sub for super::Timestamp {
type Rhs = super::Interval;
type Output = super::Timestamp;
impl<T> Add for Nullable<T>
where
T: Add + NotNull,
T::Rhs: NotNull,
T::Output: NotNull,
{
type Rhs = Nullable<T::Rhs>;
type Output = Nullable<T::Output>;
}
impl Sub for super::Nullable<super::Timestamp> {
type Rhs = super::Nullable<super::Interval>;
type Output = super::Nullable<super::Timestamp>;
impl<T> Sub for Nullable<T>
where
T: Sub + NotNull,
T::Rhs: NotNull,
T::Output: NotNull,
{
type Rhs = Nullable<T::Rhs>;
type Output = Nullable<T::Output>;
}
impl<T> Mul for Nullable<T>
where
T: Mul + NotNull,
T::Rhs: NotNull,
T::Output: NotNull,
{
type Rhs = Nullable<T::Rhs>;
type Output = Nullable<T::Output>;
}
impl<T> Div for Nullable<T>
where
T: Div + NotNull,
T::Rhs: NotNull,
T::Output: NotNull,
{
type Rhs = Nullable<T::Rhs>;
type Output = Nullable<T::Output>;
}

View File

@ -0,0 +1,78 @@
use quote;
use syn;
use util::*;
pub fn derive(mut item: syn::DeriveInput) -> Result<quote::Tokens, Diagnostic> {
let struct_name = item.ident;
{
let where_clause = item.generics
.where_clause
.get_or_insert(parse_quote!(where));
where_clause.predicates.push(parse_quote!(Self: Expression));
where_clause.predicates.push_punct(Default::default());
}
let (_, ty_generics, where_clause) = item.generics.split_for_impl();
let mut impl_generics = item.generics.clone();
impl_generics.params.push(parse_quote!(__Rhs));
let (impl_generics, _, _) = impl_generics.split_for_impl();
let dummy_name = format!("_impl_diesel_numeric_ops_for_{}", item.ident);
Ok(wrap_in_dummy_mod(
dummy_name.to_lowercase().into(),
quote! {
use self::diesel::expression::{ops, Expression, AsExpression};
use self::diesel::sql_types::ops::{Add, Sub, Mul, Div};
impl #impl_generics self::std::ops::Add<__Rhs> for #struct_name #ty_generics
#where_clause
<Self as Expression>::SqlType: Add,
__Rhs: AsExpression<<<Self as Expression>::SqlType as Add>::Rhs>,
{
type Output = ops::Add<Self, __Rhs::Expression>;
fn add(self, rhs: __Rhs) -> Self::Output {
ops::Add::new(self, rhs.as_expression())
}
}
impl #impl_generics self::std::ops::Sub<__Rhs> for #struct_name #ty_generics
#where_clause
<Self as Expression>::SqlType: Sub,
__Rhs: AsExpression<<<Self as Expression>::SqlType as Sub>::Rhs>,
{
type Output = ops::Sub<Self, __Rhs::Expression>;
fn sub(self, rhs: __Rhs) -> Self::Output {
ops::Sub::new(self, rhs.as_expression())
}
}
impl #impl_generics self::std::ops::Mul<__Rhs> for #struct_name #ty_generics
#where_clause
<Self as Expression>::SqlType: Mul,
__Rhs: AsExpression<<<Self as Expression>::SqlType as Mul>::Rhs>,
{
type Output = ops::Mul<Self, __Rhs::Expression>;
fn mul(self, rhs: __Rhs) -> Self::Output {
ops::Mul::new(self, rhs.as_expression())
}
}
impl #impl_generics self::std::ops::Div<__Rhs> for #struct_name #ty_generics
#where_clause
<Self as Expression>::SqlType: Div,
__Rhs: AsExpression<<<Self as Expression>::SqlType as Div>::Rhs>,
{
type Output = ops::Div<Self, __Rhs::Expression>;
fn div(self, rhs: __Rhs) -> Self::Output {
ops::Div::new(self, rhs.as_expression())
}
}
},
))
}

View File

@ -30,6 +30,7 @@ mod util;
mod as_changeset;
mod as_expression;
mod associations;
mod diesel_numeric_ops;
mod from_sql_row;
mod identifiable;
mod insertable;
@ -56,6 +57,11 @@ pub fn derive_associations(input: TokenStream) -> TokenStream {
expand_derive(input, associations::derive)
}
#[proc_macro_derive(DieselNumericOps)]
pub fn derive_diesel_numeric_ops(input: TokenStream) -> TokenStream {
expand_derive(input, diesel_numeric_ops::derive)
}
#[proc_macro_derive(FromSqlRow, attributes(diesel))]
pub fn derive_from_sql_row(input: TokenStream) -> TokenStream {
expand_derive(input, from_sql_row::derive)