diff --git a/CHANGELOG.md b/CHANGELOG.md index ca6b02387689..b5bf488e966b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,12 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/ [transaction-0.11.0]: http://docs.diesel.rs/diesel/connection/trait.Connection.html#method.transaction +* The way tuples of columns from the right side of left outer joins interact + with `.select` has changed. If you are deserializing into an option of a tuple + (instead of a tuple of options), you will need to explicitly call + `.nullable()`. (e.g. `.select(users::name, (posts::title, + posts::body).nullable())`) + ### Removed * `result::TransactionError` diff --git a/diesel/src/expression/aliased.rs b/diesel/src/expression/aliased.rs index d6eace25d60b..20f3f315984f 100644 --- a/diesel/src/expression/aliased.rs +++ b/diesel/src/expression/aliased.rs @@ -20,9 +20,6 @@ impl<'a, Expr> Aliased<'a, Expr> { } } -#[derive(Debug, Copy, Clone)] -pub struct FromEverywhere; - impl<'a, T> Expression for Aliased<'a, T> where T: Expression, { @@ -57,7 +54,9 @@ impl<'a, T> QueryId for Aliased<'a, T> { // FIXME This is incorrect, should only be selectable from WithQuerySource impl<'a, T, QS> SelectableExpression for Aliased<'a, T> where Aliased<'a, T>: Expression, + T: SelectableExpression, { + type SqlTypeForSelect = T::SqlTypeForSelect; } impl<'a, T: Expression + Copy> QuerySource for Aliased<'a, T> { diff --git a/diesel/src/expression/array_comparison.rs b/diesel/src/expression/array_comparison.rs index 15e33601e9dd..5a06df87d994 100644 --- a/diesel/src/expression/array_comparison.rs +++ b/diesel/src/expression/array_comparison.rs @@ -57,6 +57,7 @@ impl SelectableExpression for In where T: SelectableExpression, U: SelectableExpression, { + type SqlTypeForSelect = Self::SqlType; } impl SelectableExpression for NotIn where @@ -64,6 +65,7 @@ impl SelectableExpression for NotIn where T: SelectableExpression, U: SelectableExpression, { + type SqlTypeForSelect = Self::SqlType; } impl NonAggregate for In where @@ -163,9 +165,10 @@ pub trait MaybeEmpty { fn is_empty(&self) -> bool; } -impl AsInExpression - for SelectStatement where - Subselect, ST>: Expression, +impl AsInExpression + for SelectStatement where + SelectStatement: Query, + Subselect, ST>: Expression, { type InExpression = Subselect; @@ -202,6 +205,7 @@ impl SelectableExpression for Many where Many: Expression, T: SelectableExpression, { + type SqlTypeForSelect = T::SqlTypeForSelect; } impl QueryFragment for Many where @@ -251,6 +255,7 @@ impl SelectableExpression for Subselect where Subselect: Expression, T: Query, { + type SqlTypeForSelect = ST; } impl QueryFragment for Subselect where diff --git a/diesel/src/expression/bound.rs b/diesel/src/expression/bound.rs index d4e3587fa47c..5ff48d4e7f3d 100644 --- a/diesel/src/expression/bound.rs +++ b/diesel/src/expression/bound.rs @@ -66,6 +66,7 @@ impl QueryId for Bound { impl SelectableExpression for Bound where Bound: Expression, { + type SqlTypeForSelect = T; } impl NonAggregate for Bound where diff --git a/diesel/src/expression/coerce.rs b/diesel/src/expression/coerce.rs index 665367a04e78..a6735bc94042 100644 --- a/diesel/src/expression/coerce.rs +++ b/diesel/src/expression/coerce.rs @@ -41,6 +41,7 @@ impl Expression for Coerce where impl SelectableExpression for Coerce where T: SelectableExpression, { + type SqlTypeForSelect = Self::SqlType; } impl QueryFragment for Coerce where diff --git a/diesel/src/expression/count.rs b/diesel/src/expression/count.rs index 80d48a5c21c7..691d02d33aeb 100644 --- a/diesel/src/expression/count.rs +++ b/diesel/src/expression/count.rs @@ -80,6 +80,7 @@ impl, DB: Backend> QueryFragment for Count { impl_query_id!(Count); impl SelectableExpression for Count { + type SqlTypeForSelect = BigInt; } #[derive(Debug, Clone, Copy)] @@ -106,6 +107,7 @@ impl QueryFragment for CountStar { } impl SelectableExpression for CountStar { + type SqlTypeForSelect = BigInt; } impl_query_id!(CountStar); diff --git a/diesel/src/expression/exists.rs b/diesel/src/expression/exists.rs index f0047369fc4b..79d56981674a 100644 --- a/diesel/src/expression/exists.rs +++ b/diesel/src/expression/exists.rs @@ -52,6 +52,7 @@ impl Expression for Exists where impl SelectableExpression for Exists where Exists: Expression, { + type SqlTypeForSelect = Bool; } impl NonAggregate for Exists { diff --git a/diesel/src/expression/expression_methods/global_expression_methods.rs b/diesel/src/expression/expression_methods/global_expression_methods.rs index 95f7e6ac5234..a462a3541b50 100644 --- a/diesel/src/expression/expression_methods/global_expression_methods.rs +++ b/diesel/src/expression/expression_methods/global_expression_methods.rs @@ -307,7 +307,7 @@ pub trait ExpressionMethods: Expression + Sized { /// # fn main() { /// # use self::users::dsl::*; /// # let order = "name"; - /// let ordering: Box> = + /// let ordering: Box> = /// if order == "name" { /// Box::new(name.desc()) /// } else { diff --git a/diesel/src/expression/functions/aggregate_folding.rs b/diesel/src/expression/functions/aggregate_folding.rs index 6e59e8a658ba..bd5a825cba21 100644 --- a/diesel/src/expression/functions/aggregate_folding.rs +++ b/diesel/src/expression/functions/aggregate_folding.rs @@ -51,10 +51,11 @@ macro_rules! fold_function { impl_query_id!($type_name); - impl SelectableExpression for $type_name where - ST: Foldable, - T: Expression, + impl SelectableExpression for $type_name where + $type_name: Expression, + T: SelectableExpression, { + type SqlTypeForSelect = Self::SqlType; } } } diff --git a/diesel/src/expression/functions/aggregate_ordering.rs b/diesel/src/expression/functions/aggregate_ordering.rs index d497edc0da97..19d05466d482 100644 --- a/diesel/src/expression/functions/aggregate_ordering.rs +++ b/diesel/src/expression/functions/aggregate_ordering.rs @@ -52,7 +52,9 @@ macro_rules! ord_function { impl SelectableExpression for $type_name where $type_name: Expression, + T: SelectableExpression, { + type SqlTypeForSelect = Self::SqlType; } } } diff --git a/diesel/src/expression/functions/date_and_time.rs b/diesel/src/expression/functions/date_and_time.rs index 01dd04964e45..e1c091476b89 100644 --- a/diesel/src/expression/functions/date_and_time.rs +++ b/diesel/src/expression/functions/date_and_time.rs @@ -15,6 +15,7 @@ impl Expression for now { } impl SelectableExpression for now { + type SqlTypeForSelect = Timestamp; } impl NonAggregate for now { diff --git a/diesel/src/expression/functions/mod.rs b/diesel/src/expression/functions/mod.rs index b80839cb6ad8..0d49dbf6f180 100644 --- a/diesel/src/expression/functions/mod.rs +++ b/diesel/src/expression/functions/mod.rs @@ -65,6 +65,7 @@ macro_rules! sql_function_body { $($arg_name: $crate::expression::SelectableExpression,)* $struct_name<$($arg_name),*>: $crate::expression::Expression, { + type SqlTypeForSelect = Self::SqlType; } #[allow(non_camel_case_types)] @@ -132,6 +133,7 @@ macro_rules! no_arg_sql_function_body_except_to_sql { } impl $crate::expression::SelectableExpression for $type_name { + type SqlTypeForSelect = $return_type; } impl $crate::expression::NonAggregate for $type_name { diff --git a/diesel/src/expression/grouped.rs b/diesel/src/expression/grouped.rs index 30f8bcd45327..9ef0688d3993 100644 --- a/diesel/src/expression/grouped.rs +++ b/diesel/src/expression/grouped.rs @@ -34,6 +34,7 @@ impl SelectableExpression for Grouped where T: SelectableExpression, Grouped: Expression, { + type SqlTypeForSelect = T::SqlTypeForSelect; } impl NonAggregate for Grouped where diff --git a/diesel/src/expression/mod.rs b/diesel/src/expression/mod.rs index b7d97661b868..7b6857d759df 100644 --- a/diesel/src/expression/mod.rs +++ b/diesel/src/expression/mod.rs @@ -104,29 +104,30 @@ impl AsExpression for T { } } -/// Indicates that an expression can be selected from a source. The second type -/// argument is optional, but is used to indicate that the right side of a left -/// outer join is nullable, even if it wasn't before. +/// Indicates that an expression can be selected from a source. The associated +/// type is usually the same as `Expression::SqlType`, but is used to indicate +/// that a column is always nullable when it appears on the right side of a left +/// outer join, even if it wasn't nullable to begin with. /// /// Columns will implement this for their table. Certain special types, like /// `CountStar` and `Bound` will implement this for all sources. All other /// expressions will inherit this from their children. -pub trait SelectableExpression< - QS, - Type = ::SqlType, ->: Expression { +pub trait SelectableExpression: Expression { + type SqlTypeForSelect; } -impl SelectableExpression for Box where - T: SelectableExpression, +impl SelectableExpression for Box where + T: SelectableExpression, Box: Expression, { + type SqlTypeForSelect = T::SqlTypeForSelect; } -impl<'a, T: ?Sized, ST, QS> SelectableExpression for &'a T where - T: SelectableExpression, +impl<'a, T: ?Sized, QS> SelectableExpression for &'a T where + T: SelectableExpression, &'a T: Expression, { + type SqlTypeForSelect = T::SqlTypeForSelect; } /// Marker trait to indicate that an expression does not include any aggregate @@ -147,24 +148,24 @@ use query_builder::{QueryFragment, QueryId}; /// Helper trait used when boxing expressions. This exists to work around the /// fact that Rust will not let us use non-core types as bounds on a trait /// object (you could not return `Box`) -pub trait BoxableExpression where +pub trait BoxableExpression where DB: Backend, Self: Expression, - Self: SelectableExpression, + Self: SelectableExpression, Self: NonAggregate, Self: QueryFragment, {} -impl BoxableExpression for T where +impl BoxableExpression for T where DB: Backend, T: Expression, - T: SelectableExpression, + T: SelectableExpression, T: NonAggregate, T: QueryFragment, { } -impl QueryId for BoxableExpression { +impl QueryId for BoxableExpression { type QueryId = (); fn has_static_query_id() -> bool { diff --git a/diesel/src/expression/nullable.rs b/diesel/src/expression/nullable.rs index 0ebea3f117e6..7095eeea1acb 100644 --- a/diesel/src/expression/nullable.rs +++ b/diesel/src/expression/nullable.rs @@ -37,10 +37,14 @@ impl QueryFragment for Nullable where } } +/// This impl relies on the fact that the only time `T::SqlType` will differ +/// from `T::SqlTypeForSelect` is to make the right side of a left join become +/// nullable. impl SelectableExpression for Nullable where T: SelectableExpression, Nullable: Expression, { + type SqlTypeForSelect = Self::SqlType; } impl QueryId for Nullable { diff --git a/diesel/src/expression/ops/numeric.rs b/diesel/src/expression/ops/numeric.rs index 1f6522fa146c..ed86bc104f40 100644 --- a/diesel/src/expression/ops/numeric.rs +++ b/diesel/src/expression/ops/numeric.rs @@ -59,6 +59,7 @@ macro_rules! numeric_operation { Rhs: SelectableExpression, $name: Expression, { + type SqlTypeForSelect = Self::SqlType; } impl NonAggregate for $name where diff --git a/diesel/src/expression/predicates.rs b/diesel/src/expression/predicates.rs index c1fb0ab2a2b7..1cfd96c597e0 100644 --- a/diesel/src/expression/predicates.rs +++ b/diesel/src/expression/predicates.rs @@ -30,6 +30,7 @@ macro_rules! infix_predicate_body { T: $crate::expression::SelectableExpression, U: $crate::expression::SelectableExpression, { + type SqlTypeForSelect = Self::SqlType; } impl $crate::expression::NonAggregate for $name where @@ -219,6 +220,7 @@ macro_rules! postfix_predicate_body { impl $crate::expression::SelectableExpression for $name where T: $crate::expression::SelectableExpression, { + type SqlTypeForSelect = Self::SqlType; } impl $crate::expression::NonAggregate for $name where diff --git a/diesel/src/expression/sql_literal.rs b/diesel/src/expression/sql_literal.rs index 7889786f218b..1e98109c8b30 100644 --- a/diesel/src/expression/sql_literal.rs +++ b/diesel/src/expression/sql_literal.rs @@ -51,6 +51,7 @@ impl Query for SqlLiteral { } impl SelectableExpression for SqlLiteral { + type SqlTypeForSelect = ST; } impl NonAggregate for SqlLiteral { diff --git a/diesel/src/macros/mod.rs b/diesel/src/macros/mod.rs index 243c7e178f7f..4dbb0d991cad 100644 --- a/diesel/src/macros/mod.rs +++ b/diesel/src/macros/mod.rs @@ -31,19 +31,24 @@ macro_rules! __diesel_column { impl_query_id!($column_name); - impl $crate::expression::SelectableExpression<$($table)::*> for $column_name {} + impl $crate::expression::SelectableExpression<$($table)::*> for $column_name { + type SqlTypeForSelect = $Type; + } - impl<'a, ST, Left, Right> SelectableExpression< - $crate::WithQuerySource<'a, Left, Right>, ST> for $column_name where - $column_name: SelectableExpression + impl<'a, Left, Right> SelectableExpression< + $crate::WithQuerySource<'a, Left, Right>, + > for $column_name where + $column_name: SelectableExpression, { + type SqlTypeForSelect = $Type; } - impl SelectableExpression< - $crate::query_source::filter::FilteredQuerySource, ST> - for $column_name where - $column_name: SelectableExpression + impl SelectableExpression< + $crate::query_source::filter::FilteredQuerySource, + > for $column_name where + $column_name: SelectableExpression, { + type SqlTypeForSelect = $Type; } impl $crate::expression::NonAggregate for $column_name {} @@ -301,7 +306,7 @@ macro_rules! table_body { impl AsQuery for table { type SqlType = SqlType; - type Query = SelectStatement; + type Query = SelectStatement<($($column_name,)+), Self>; fn as_query(self) -> Self::Query { SelectStatement::simple(all_columns, self) @@ -376,7 +381,9 @@ macro_rules! table_body { } } - impl SelectableExpression for star {} + impl SelectableExpression
for star { + type SqlTypeForSelect = Self::SqlType; + } $(__diesel_column!(table, $column_name -> $column_ty);)+ } @@ -485,26 +492,29 @@ macro_rules! select_column_inner { $crate::query_source::InnerJoinSource<$child, $parent>, > for $column { + type SqlTypeForSelect = ::SqlType; } impl $crate::expression::SelectableExpression< $crate::query_source::InnerJoinSource<$parent, $child>, > for $column { + type SqlTypeForSelect = ::SqlType; } impl $crate::expression::SelectableExpression< $crate::query_source::LeftOuterJoinSource<$child, $parent>, - <<$column as $crate::Expression>::SqlType - as $crate::types::IntoNullable>::Nullable, > for $column { + type SqlTypeForSelect = <::SqlType + as $crate::types::IntoNullable>::Nullable; } impl $crate::expression::SelectableExpression< $crate::query_source::LeftOuterJoinSource<$parent, $child>, > for $column { + type SqlTypeForSelect = ::SqlType; } } } diff --git a/diesel/src/pg/expression/array_comparison.rs b/diesel/src/pg/expression/array_comparison.rs index 9da7f505c73f..728824e4cab2 100644 --- a/diesel/src/pg/expression/array_comparison.rs +++ b/diesel/src/pg/expression/array_comparison.rs @@ -1,12 +1,10 @@ -use std::marker::PhantomData; - use backend::*; use expression::{AsExpression, Expression, SelectableExpression, NonAggregate}; use pg::{Pg, PgQueryBuilder}; use query_builder::*; use query_builder::debug::DebugQueryBuilder; use result::QueryResult; -use types::{Array, HasSqlType}; +use types::Array; /// Creates a PostgreSQL `ANY` expression. /// @@ -38,8 +36,7 @@ use types::{Array, HasSqlType}; /// assert_eq!(Ok(vec![sean, jim]), data.load(&connection)); /// # } /// ``` -pub fn any(vals: T) -> Any where - Pg: HasSqlType, +pub fn any(vals: T) -> Any where T: AsExpression>, { Any::new(vals.as_expression()) @@ -74,8 +71,7 @@ pub fn any(vals: T) -> Any where /// assert_eq!(Ok(vec![tess]), data.load(&connection)); /// # } /// ``` -pub fn all(vals: T) -> All where - Pg: HasSqlType, +pub fn all(vals: T) -> All where T: AsExpression>, { All::new(vals.as_expression()) @@ -83,28 +79,25 @@ pub fn all(vals: T) -> All where #[doc(hidden)] #[derive(Debug, Copy, Clone)] -pub struct Any { +pub struct Any { expr: Expr, - _marker: PhantomData, } -impl Any { +impl Any { fn new(expr: Expr) -> Self { Any { expr: expr, - _marker: PhantomData, } } } -impl Expression for Any where - Pg: HasSqlType, +impl Expression for Any where Expr: Expression>, { type SqlType = ST; } -impl QueryFragment for Any where +impl QueryFragment for Any where Expr: QueryFragment, { fn to_sql(&self, out: &mut PgQueryBuilder) -> BuildQueryResult { @@ -124,7 +117,7 @@ impl QueryFragment for Any where } } -impl QueryFragment for Any where +impl QueryFragment for Any where Expr: QueryFragment, { fn to_sql(&self, out: &mut DebugQueryBuilder) -> BuildQueryResult { @@ -144,45 +137,41 @@ impl QueryFragment for Any where } } -impl_query_id!(Any); +impl_query_id!(Any); -impl SelectableExpression for Any where - Pg: HasSqlType, - Any: Expression, +impl SelectableExpression for Any where + Any: Expression, Expr: SelectableExpression, { + type SqlTypeForSelect = Self::SqlType; } -impl NonAggregate for Any where +impl NonAggregate for Any where Expr: NonAggregate, - Any: Expression, { } #[doc(hidden)] #[derive(Debug, Copy, Clone)] -pub struct All { +pub struct All { expr: Expr, - _marker: PhantomData, } -impl All { +impl All { fn new(expr: Expr) -> Self { All { expr: expr, - _marker: PhantomData, } } } -impl Expression for All where - Pg: HasSqlType, +impl Expression for All where Expr: Expression>, { type SqlType = ST; } -impl QueryFragment for All where +impl QueryFragment for All where Expr: QueryFragment, { fn to_sql(&self, out: &mut PgQueryBuilder) -> BuildQueryResult { @@ -202,7 +191,7 @@ impl QueryFragment for All where } } -impl QueryFragment for All where +impl QueryFragment for All where Expr: QueryFragment, { fn to_sql(&self, out: &mut DebugQueryBuilder) -> BuildQueryResult { @@ -222,17 +211,16 @@ impl QueryFragment for All where } } -impl_query_id!(All); +impl_query_id!(All); -impl SelectableExpression for All where - Pg: HasSqlType, - All: Expression, +impl SelectableExpression for All where + All: Expression, Expr: SelectableExpression, { + type SqlTypeForSelect = Self::SqlType; } -impl NonAggregate for All where +impl NonAggregate for All where Expr: NonAggregate, - All: Expression, { } diff --git a/diesel/src/pg/expression/date_and_time.rs b/diesel/src/pg/expression/date_and_time.rs index 1bb5e4cd694b..bff5287b211c 100644 --- a/diesel/src/pg/expression/date_and_time.rs +++ b/diesel/src/pg/expression/date_and_time.rs @@ -88,5 +88,7 @@ impl QueryFragment for AtTimeZone where impl SelectableExpression for AtTimeZone where AtTimeZone: Expression, Ts: SelectableExpression, - Tz: SelectableExpression, -{} + Tz: SelectableExpression, +{ + type SqlTypeForSelect = Self::SqlType; +} diff --git a/diesel/src/query_builder/functions.rs b/diesel/src/query_builder/functions.rs index ba605e852986..e24592b5f63f 100644 --- a/diesel/src/query_builder/functions.rs +++ b/diesel/src/query_builder/functions.rs @@ -115,7 +115,7 @@ pub fn insert(records: &T) -> IncompleteInsertStatement<&T, Insert> { /// Creates a bare select statement, with no from clause. Primarily used for /// testing diesel itself, but likely useful for third party crates as well. The /// given expressions must be selectable from anywhere. -pub fn select(expression: T) -> SelectStatement where +pub fn select(expression: T) -> SelectStatement where T: Expression, { SelectStatement::simple(expression, ()) diff --git a/diesel/src/query_builder/select_statement/boxed.rs b/diesel/src/query_builder/select_statement/boxed.rs index d7aed6c69b99..04727588e2da 100644 --- a/diesel/src/query_builder/select_statement/boxed.rs +++ b/diesel/src/query_builder/select_statement/boxed.rs @@ -20,7 +20,7 @@ pub struct BoxedSelectStatement<'a, ST, QS, DB> { order: Box + 'a>, limit: Box + 'a>, offset: Box + 'a>, - _marker: PhantomData<(ST, DB)>, + _marker: PhantomData, } impl<'a, ST, QS, DB> BoxedSelectStatement<'a, ST, QS, DB> { @@ -153,12 +153,12 @@ impl<'a, ST, QS, DB> QueryId for BoxedSelectStatement<'a, ST, QS, DB> { } } -impl<'a, ST, QS, DB, Type, Selection> SelectDsl +impl<'a, ST, QS, DB, Selection> SelectDsl for BoxedSelectStatement<'a, ST, QS, DB> where - DB: Backend + HasSqlType, - Selection: SelectableExpression + QueryFragment + 'a, + DB: Backend + HasSqlType, + Selection: SelectableExpression + QueryFragment + 'a, { - type Output = BoxedSelectStatement<'a, Type, QS, DB>; + type Output = BoxedSelectStatement<'a, Selection::SqlTypeForSelect, QS, DB>; fn select(self, selection: Selection) -> Self::Output { BoxedSelectStatement::new( diff --git a/diesel/src/query_builder/select_statement/dsl_impls.rs b/diesel/src/query_builder/select_statement/dsl_impls.rs index 2d26b740bc3f..737f0372c588 100644 --- a/diesel/src/query_builder/select_statement/dsl_impls.rs +++ b/diesel/src/query_builder/select_statement/dsl_impls.rs @@ -13,12 +13,12 @@ use query_dsl::boxed_dsl::InternalBoxedDsl; use super::BoxedSelectStatement; use types::{self, Bool}; -impl SelectDsl - for SelectStatement where +impl SelectDsl + for SelectStatement where Selection: Expression, - SelectStatement: Query, + SelectStatement: Query, { - type Output = SelectStatement; + type Output = SelectStatement; fn select(self, selection: Selection) -> Self::Output { SelectStatement::new( @@ -35,11 +35,11 @@ impl SelectDsl } impl DistinctDsl - for SelectStatement where - SelectStatement: AsQuery, - SelectStatement: AsQuery, + for SelectStatement where + SelectStatement: AsQuery, + SelectStatement: AsQuery, { - type Output = SelectStatement; + type Output = SelectStatement; fn distinct(self) -> Self::Output { SelectStatement::new( @@ -56,13 +56,13 @@ impl DistinctDsl } impl FilterDsl - for SelectStatement where - SelectStatement: AsQuery, - SelectStatement: Query, + for SelectStatement where + SelectStatement: AsQuery, + SelectStatement: Query, Predicate: SelectableExpression + NonAggregate, W: WhereAnd, { - type Output = SelectStatement; + type Output = SelectStatement; fn filter(self, predicate: Predicate) -> Self::Output { SelectStatement::new( @@ -79,12 +79,12 @@ impl FilterDsl } impl OrderDsl - for SelectStatement where + for SelectStatement where Expr: SelectableExpression, - SelectStatement: AsQuery, - SelectStatement, L, Of, G>: AsQuery, + SelectStatement: AsQuery, + SelectStatement, L, Of, G>: AsQuery, { - type Output = SelectStatement, L, Of, G>; + type Output = SelectStatement, L, Of, G>; fn order(self, expr: Expr) -> Self::Output { let order = OrderClause(expr); @@ -105,11 +105,11 @@ impl OrderDsl pub type Limit = >::Expression; impl LimitDsl - for SelectStatement where - SelectStatement: AsQuery, - SelectStatement, Of, G>: Query, + for SelectStatement where + SelectStatement: AsQuery, + SelectStatement, Of, G>: Query, { - type Output = SelectStatement, Of, G>; + type Output = SelectStatement, Of, G>; fn limit(self, limit: i64) -> Self::Output { let limit_clause = LimitClause(AsExpression::::as_expression(limit)); @@ -130,11 +130,11 @@ impl LimitDsl pub type Offset = Limit; impl OffsetDsl - for SelectStatement where - SelectStatement: AsQuery, - SelectStatement, G>: AsQuery, + for SelectStatement where + SelectStatement: AsQuery, + SelectStatement, G>: AsQuery, { - type Output = SelectStatement, G>; + type Output = SelectStatement, G>; fn offset(self, offset: i64) -> Self::Output { let offset_clause = OffsetClause(AsExpression::::as_expression(offset)); @@ -151,11 +151,11 @@ impl OffsetDsl } } -impl<'a, ST, S, F, D, W, O, L, Of, G, Expr> WithDsl<'a, Expr> - for SelectStatement where - SelectStatement, D, W, O, L, Of, G>: Query, +impl<'a, S, F, D, W, O, L, Of, G, Expr> WithDsl<'a, Expr> + for SelectStatement where + SelectStatement, D, W, O, L, Of, G>: Query, { - type Output = SelectStatement, D, W, O, L, Of, G>; + type Output = SelectStatement, D, W, O, L, Of, G>; fn with(self, expr: Aliased<'a, Expr>) -> Self::Output { let source = WithQuerySource::new(self.from, expr); @@ -172,12 +172,12 @@ impl<'a, ST, S, F, D, W, O, L, Of, G, Expr> WithDsl<'a, Expr> } } -impl GroupByDsl - for SelectStatement where - SelectStatement>: Query, +impl GroupByDsl + for SelectStatement where + SelectStatement>: Query, Expr: Expression, { - type Output = SelectStatement>; + type Output = SelectStatement>; fn group_by(self, expr: Expr) -> Self::Output { let group_by = GroupByClause(expr); @@ -194,17 +194,17 @@ impl GroupByDsl } } -impl<'a, ST, S, F, D, W, O, L, Of, G, DB> InternalBoxedDsl<'a, DB> - for SelectStatement where +impl<'a, S, F, D, W, O, L, Of, G, DB> InternalBoxedDsl<'a, DB> + for SelectStatement where DB: Backend, - S: QueryFragment + 'a, + S: QueryFragment + SelectableExpression + 'a, D: QueryFragment + 'a, W: Into + 'a>>>, O: QueryFragment + 'a, L: QueryFragment + 'a, Of: QueryFragment + 'a, { - type Output = BoxedSelectStatement<'a, ST, F, DB>; + type Output = BoxedSelectStatement<'a, S::SqlTypeForSelect, F, DB>; fn internal_into_boxed(self) -> Self::Output { BoxedSelectStatement::new( diff --git a/diesel/src/query_builder/select_statement/mod.rs b/diesel/src/query_builder/select_statement/mod.rs index b3e38477daa5..7a25bbddf45c 100644 --- a/diesel/src/query_builder/select_statement/mod.rs +++ b/diesel/src/query_builder/select_statement/mod.rs @@ -3,8 +3,6 @@ mod boxed; pub use self::boxed::BoxedSelectStatement; -use std::marker::PhantomData; - use backend::Backend; use expression::*; use query_source::*; @@ -20,7 +18,6 @@ use super::{Query, QueryBuilder, QueryFragment, BuildQueryResult}; #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct SelectStatement< - SqlType, Select, From, Distinct = NoDistinctClause, @@ -38,10 +35,9 @@ pub struct SelectStatement< limit: Limit, offset: Offset, group_by: GroupBy, - _marker: PhantomData, } -impl SelectStatement { +impl SelectStatement { #[cfg_attr(feature = "clippy", allow(too_many_arguments))] pub fn new( select: S, @@ -62,12 +58,11 @@ impl SelectStatement { limit: limit, offset: offset, group_by: group_by, - _marker: PhantomData, } } pub fn inner_join(self, other: T) - -> SelectStatement, D, W, O, L, Of, G> where + -> SelectStatement, D, W, O, L, Of, G> where T: Table, F: Table + JoinTo, { @@ -84,7 +79,7 @@ impl SelectStatement { } pub fn left_outer_join(self, other: T) - -> SelectStatement, D, W, O, L, Of, G> where + -> SelectStatement, D, W, O, L, Of, G> where T: Table, F: Table + JoinTo, { @@ -101,7 +96,7 @@ impl SelectStatement { } } -impl SelectStatement { +impl SelectStatement { pub fn simple(select: S, from: F) -> Self { SelectStatement::new( select, @@ -116,31 +111,31 @@ impl SelectStatement { } } -impl Query - for SelectStatement where - S: SelectableExpression, +impl Query + for SelectStatement where + S: SelectableExpression, { - type SqlType = ST; + type SqlType = S::SqlTypeForSelect; } #[cfg(feature = "postgres")] -impl Expression - for SelectStatement where - S: SelectableExpression, +impl Expression + for SelectStatement where + S: SelectableExpression, { - type SqlType = ::types::Array; + type SqlType = ::types::Array; } #[cfg(not(feature = "postgres"))] -impl Expression - for SelectStatement where - S: SelectableExpression, +impl Expression + for SelectStatement where + S: SelectableExpression, { - type SqlType = ST; + type SqlType = S::SqlTypeForSelect; } -impl QueryFragment - for SelectStatement where +impl QueryFragment + for SelectStatement where DB: Backend, S: QueryFragment, F: QuerySource, @@ -190,8 +185,8 @@ impl QueryFragment } } -impl QueryFragment - for SelectStatement where +impl QueryFragment + for SelectStatement where DB: Backend, S: QueryFragment, D: QueryFragment, @@ -235,16 +230,17 @@ impl QueryFragment } } -impl_query_id!(SelectStatement); +impl_query_id!(SelectStatement); -impl SelectableExpression - for SelectStatement where - SelectStatement: Expression, +impl SelectableExpression + for SelectStatement where + SelectStatement: Expression, { + type SqlTypeForSelect = Self::SqlType; } -impl NonAggregate - for SelectStatement where - SelectStatement: Expression, +impl NonAggregate + for SelectStatement where + SelectStatement: Expression, { } diff --git a/diesel/src/query_source/joins.rs b/diesel/src/query_source/joins.rs index 86f6b89fec08..fccf96af779f 100644 --- a/diesel/src/query_source/joins.rs +++ b/diesel/src/query_source/joins.rs @@ -1,4 +1,6 @@ +use prelude::*; use expression::SelectableExpression; +use expression::nullable::Nullable; use query_builder::*; use result::QueryResult; use super::{QuerySource, Table}; @@ -35,13 +37,12 @@ impl AsQuery for InnerJoinSource where Left: Table + JoinTo, Right: Table, (Left::AllColumns, Right::AllColumns): SelectableExpression< - InnerJoinSource, - (Left::SqlType, Right::SqlType), - >, + InnerJoinSource, + SqlTypeForSelect=(Left::SqlType, Right::SqlType), + >, { type SqlType = (Left::SqlType, Right::SqlType); type Query = SelectStatement< - (Left::SqlType, Right::SqlType), (Left::AllColumns, Right::AllColumns), Self, >; @@ -84,20 +85,22 @@ impl AsQuery for LeftOuterJoinSource where Left: Table + JoinTo, Right: Table, Right::SqlType: IntoNullable, - (Left::AllColumns, Right::AllColumns): SelectableExpression< - LeftOuterJoinSource, - (Left::SqlType, ::Nullable), - >, + (Left::AllColumns, Nullable): SelectableExpression< + LeftOuterJoinSource, + SqlTypeForSelect=(Left::SqlType, ::Nullable) + >, { type SqlType = (Left::SqlType, ::Nullable); type Query = SelectStatement< - Self::SqlType, - (Left::AllColumns, Right::AllColumns), + (Left::AllColumns, Nullable), Self, >; fn as_query(self) -> Self::Query { - SelectStatement::simple((Left::all_columns(), Right::all_columns()), self) + SelectStatement::simple( + (Left::all_columns(), Right::all_columns().nullable()), + self, + ) } } diff --git a/diesel/src/types/impls/tuples.rs b/diesel/src/types/impls/tuples.rs index ff4eaf4381c7..c40b7327668b 100644 --- a/diesel/src/types/impls/tuples.rs +++ b/diesel/src/types/impls/tuples.rs @@ -210,21 +210,11 @@ macro_rules! tuple_impls { } } - impl<$($T),+, $($ST),+, QS> - SelectableExpression - for ($($T,)+) where - $($T: SelectableExpression),+, - ($($T,)+): Expression, - { - } - - impl<$($T),+, $($ST),+, QS> - SelectableExpression> - for ($($T,)+) where - $($ST: IntoNullable,)+ - $($T: SelectableExpression),+, + impl<$($T,)+ QS> SelectableExpression for ($($T,)+) where + $($T: SelectableExpression,)+ ($($T,)+): Expression, { + type SqlTypeForSelect = ($($T::SqlTypeForSelect,)+); } impl AsChangeset for ($($T,)+) where diff --git a/diesel_compile_tests/tests/compile-fail/aggregate_expression_requires_column_from_same_table.rs b/diesel_compile_tests/tests/compile-fail/aggregate_expression_requires_column_from_same_table.rs new file mode 100644 index 000000000000..e21a14ea1c30 --- /dev/null +++ b/diesel_compile_tests/tests/compile-fail/aggregate_expression_requires_column_from_same_table.rs @@ -0,0 +1,28 @@ +#[macro_use] +extern crate diesel; + +use diesel::*; + +table! { + users { + id -> Integer, + } +} + +table! { + posts { + id -> Integer, + } +} + +fn main() { + use diesel::expression::dsl::*; + let source = users::table.select(sum(posts::id)); + //~^ ERROR E0277 + let source = users::table.select(avg(posts::id)); + //~^ ERROR E0277 + let source = users::table.select(max(posts::id)); + //~^ ERROR E0277 + let source = users::table.select(min(posts::id)); + //~^ ERROR E0277 +} diff --git a/diesel_compile_tests/tests/compile-fail/cannot_mix_aggregate_and_non_aggregate_selects.rs b/diesel_compile_tests/tests/compile-fail/cannot_mix_aggregate_and_non_aggregate_selects.rs index beec1e8316f4..6db1281cce4e 100644 --- a/diesel_compile_tests/tests/compile-fail/cannot_mix_aggregate_and_non_aggregate_selects.rs +++ b/diesel_compile_tests/tests/compile-fail/cannot_mix_aggregate_and_non_aggregate_selects.rs @@ -15,11 +15,4 @@ fn main() { let source = users.select((id, count(users.star()))); //~^ ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 } diff --git a/diesel_compile_tests/tests/compile-fail/find_requires_correct_type.rs b/diesel_compile_tests/tests/compile-fail/find_requires_correct_type.rs index 6ad648d5710b..e14d8efbdee8 100644 --- a/diesel_compile_tests/tests/compile-fail/find_requires_correct_type.rs +++ b/diesel_compile_tests/tests/compile-fail/find_requires_correct_type.rs @@ -28,5 +28,4 @@ fn main() { //~| ERROR E0277 //~| ERROR E0277 //~| ERROR E0277 - //~| ERROR E0277 } diff --git a/diesel_compile_tests/tests/compile-fail/select_requires_column_from_same_table.rs b/diesel_compile_tests/tests/compile-fail/select_requires_column_from_same_table.rs index a16275a4c3ba..67c258de583c 100644 --- a/diesel_compile_tests/tests/compile-fail/select_requires_column_from_same_table.rs +++ b/diesel_compile_tests/tests/compile-fail/select_requires_column_from_same_table.rs @@ -20,10 +20,4 @@ table! { fn main() { let select_id = users::table.select(posts::id); //~^ ERROR SelectableExpression - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 } diff --git a/diesel_compile_tests/tests/compile-fail/selecting_multiple_columns_requires_all_must_be_from_selectable_table.rs b/diesel_compile_tests/tests/compile-fail/selecting_multiple_columns_requires_all_must_be_from_selectable_table.rs index 1ae8e067cf47..faa919cf3446 100644 --- a/diesel_compile_tests/tests/compile-fail/selecting_multiple_columns_requires_all_must_be_from_selectable_table.rs +++ b/diesel_compile_tests/tests/compile-fail/selecting_multiple_columns_requires_all_must_be_from_selectable_table.rs @@ -22,17 +22,6 @@ fn main() { let stuff = users::table.select((posts::id, posts::user_id)); //~^ ERROR Selectable //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 let stuff = users::table.select((posts::id, users::name)); //~^ ERROR Selectable - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 - //~| ERROR E0277 } diff --git a/diesel_infer_schema/src/information_schema.rs b/diesel_infer_schema/src/information_schema.rs index 1dead5ded746..5b7301268c21 100644 --- a/diesel_infer_schema/src/information_schema.rs +++ b/diesel_infer_schema/src/information_schema.rs @@ -16,7 +16,7 @@ use super::data_structures::*; pub trait UsesInformationSchema: Backend { type TypeColumn: SelectableExpression< self::information_schema::columns::table, - types::Text, + SqlTypeForSelect=types::Text, > + NonAggregate + QueryId + QueryFragment; fn type_column() -> Self::TypeColumn; diff --git a/diesel_tests/tests/expressions/mod.rs b/diesel_tests/tests/expressions/mod.rs index c8626d53e07d..096d8ce08901 100644 --- a/diesel_tests/tests/expressions/mod.rs +++ b/diesel_tests/tests/expressions/mod.rs @@ -114,7 +114,9 @@ impl QueryFragment for Arbitrary where } } -impl SelectableExpression for Arbitrary {} +impl SelectableExpression for Arbitrary { + type SqlTypeForSelect = T; +} fn arbitrary() -> Arbitrary { Arbitrary { _marker: PhantomData } diff --git a/diesel_tests/tests/filter.rs b/diesel_tests/tests/filter.rs index 61d66ab2664f..98bbc45cbc85 100644 --- a/diesel_tests/tests/filter.rs +++ b/diesel_tests/tests/filter.rs @@ -260,7 +260,7 @@ sql_function!(lower, lower_t, (x: VarChar) -> VarChar); #[test] fn filter_by_boxed_predicate() { - fn by_name(name: &str) -> Box> { + fn by_name(name: &str) -> Box> { Box::new(lower(users::name).eq(name.to_string())) } diff --git a/diesel_tests/tests/joins.rs b/diesel_tests/tests/joins.rs index 64a3502f8ecc..308c916369fb 100644 --- a/diesel_tests/tests/joins.rs +++ b/diesel_tests/tests/joins.rs @@ -128,7 +128,27 @@ fn columns_on_right_side_of_left_outer_joins_are_nullable() { } #[test] -fn select_multiple_from_right_side_returns_optional_tuple() { +fn columns_on_right_side_of_left_outer_joins_can_be_used_in_filter() { + let connection = connection_with_sean_and_tess_in_users_table(); + + connection.execute("INSERT INTO posts (user_id, title) VALUES + (1, 'Hello'), + (1, 'World') + ").unwrap(); + + let expected_data = vec![ + ("Sean".to_string(), Some("Hello".to_string())), + ]; + let source = users::table.left_outer_join(posts::table) + .select((users::name, posts::title)) + .filter(posts::title.eq("Hello")); + let actual_data: Vec<_> = source.load(&connection).unwrap(); + + assert_eq!(expected_data, actual_data); +} + +#[test] +fn select_multiple_from_right_side_returns_optional_tuple_when_nullable_is_called() { let connection = connection_with_sean_and_tess_in_users_table(); connection.execute("INSERT INTO posts (user_id, title, body) VALUES @@ -142,7 +162,7 @@ fn select_multiple_from_right_side_returns_optional_tuple() { None, ]; - let source = users::table.left_outer_join(posts::table).select((posts::title, posts::body)); + let source = users::table.left_outer_join(posts::table).select((posts::title, posts::body).nullable()); let actual_data: Vec<_> = source.load(&connection).unwrap(); assert_eq!(expected_data, actual_data); @@ -165,7 +185,7 @@ fn select_complex_from_left_join() { (tess, None), ]; - let source = users::table.left_outer_join(posts::table).select((users::all_columns, (posts::title, posts::body))); + let source = users::table.left_outer_join(posts::table).select((users::all_columns, (posts::title, posts::body).nullable())); let actual_data: Vec<_> = source.load(&connection).unwrap(); assert_eq!(expected_data, actual_data); @@ -188,7 +208,7 @@ fn select_right_side_with_nullable_column_first() { (tess, None), ]; - let source = users::table.left_outer_join(posts::table).select((users::all_columns, (posts::body, posts::title))); + let source = users::table.left_outer_join(posts::table).select((users::all_columns, (posts::body, posts::title).nullable())); let actual_data: Vec<_> = source.load(&connection).unwrap(); assert_eq!(expected_data, actual_data); diff --git a/diesel_tests/tests/types.rs b/diesel_tests/tests/types.rs index 5d8c299d983d..52b8b529607d 100644 --- a/diesel_tests/tests/types.rs +++ b/diesel_tests/tests/types.rs @@ -491,7 +491,7 @@ use diesel::query_builder::{QueryFragment, QueryId}; fn query_to_sql_equality(sql_str: &str, value: U) -> bool where TestBackend: HasSqlType, U: AsExpression + Debug + Clone, - U::Expression: SelectableExpression<(), T> + QueryFragment + QueryId, + U::Expression: SelectableExpression<()> + QueryFragment + QueryId, T: QueryId, { use diesel::expression::dsl::sql; diff --git a/diesel_tests/tests/types_roundtrip.rs b/diesel_tests/tests/types_roundtrip.rs index 63605397c47c..e467f55e14e3 100644 --- a/diesel_tests/tests/types_roundtrip.rs +++ b/diesel_tests/tests/types_roundtrip.rs @@ -17,7 +17,7 @@ pub fn test_type_round_trips(value: T) -> bool where ST: QueryId, ::Backend: HasSqlType, T: AsExpression + Queryable::Backend> + PartialEq + Clone + ::std::fmt::Debug, - >::Expression: SelectableExpression<()> + QueryFragment<::Backend> + QueryId, + >::Expression: SelectableExpression<(), SqlTypeForSelect=ST> + QueryFragment<::Backend> + QueryId, { let connection = connection(); let query = select(AsExpression::::as_expression(value.clone()));