From 200d13cf17323f91d38481943d350c3993905fba Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Wed, 15 Feb 2017 08:30:35 -0500 Subject: [PATCH 1/3] Ensure aggregate functions enforce the column is from the right table While working on #621, I noticed that these impls were incorrect and could be used to compile an incorrect query. I've corrected the impls and added the appropriate compile-fail test. I'm not sure if this was just an oversight or if I intentionally did this to avoid nullability somewhere. The latter is no longer relevant since we always make these expressions nullable now. --- .../expression/functions/aggregate_folding.rs | 6 ++-- .../functions/aggregate_ordering.rs | 1 + ...ression_requires_column_from_same_table.rs | 28 +++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 diesel_compile_tests/tests/compile-fail/aggregate_expression_requires_column_from_same_table.rs diff --git a/diesel/src/expression/functions/aggregate_folding.rs b/diesel/src/expression/functions/aggregate_folding.rs index 6e59e8a658ba..7cf24130bdd8 100644 --- a/diesel/src/expression/functions/aggregate_folding.rs +++ b/diesel/src/expression/functions/aggregate_folding.rs @@ -51,9 +51,9 @@ 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, { } } diff --git a/diesel/src/expression/functions/aggregate_ordering.rs b/diesel/src/expression/functions/aggregate_ordering.rs index d497edc0da97..c2f9507005ae 100644 --- a/diesel/src/expression/functions/aggregate_ordering.rs +++ b/diesel/src/expression/functions/aggregate_ordering.rs @@ -52,6 +52,7 @@ macro_rules! ord_function { impl SelectableExpression for $type_name where $type_name: Expression, + T: SelectableExpression, { } } 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 +} From 76383eef304fc91318c82dc912c43db883784ae8 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Wed, 15 Feb 2017 08:56:22 -0500 Subject: [PATCH 2/3] Remove unused bounds and `PhantomData` from `any` and `all` The phantom data was just plain unneccessary. It was either an oversight or a bug in older rust versions where `SqlType=Array` didn't count as the type being constrained. The `HasSqlType` constraints are sufficiently covered elsewhere (and frankly, I'm fairly certain that trait is useless and can be removed). It should be noted that we don't have a compile-test covering that case though, as pg is the only backend with additional types. --- diesel/src/pg/expression/array_comparison.rs | 56 ++++++++------------ 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/diesel/src/pg/expression/array_comparison.rs b/diesel/src/pg/expression/array_comparison.rs index 9da7f505c73f..7bd541a1e3a3 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,40 @@ 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, { } -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 +190,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 +210,15 @@ 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, { } -impl NonAggregate for All where +impl NonAggregate for All where Expr: NonAggregate, - All: Expression, { } From a4f49ddea2e6f0750fec2c897ea64054f0a2b4fe Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Wed, 15 Feb 2017 07:28:24 -0500 Subject: [PATCH 3/3] Use associated types for `SelectableExpression` The `SelectableExpression` trait serves two purposes for us. The first and most important role it fills is to ensure that columns from tables that aren't in the from clause cannot be used. The second way that we use it to make columns which are on the right side of a left outer join be nullable. There were two reasons that we used a type parameter instead of an associated type. The first was to make it so that `(Nullable, Nullable)` could be treated as `Nullable<(X, Y)>`. We did this because the return type of `users.left_outer_join(posts)` should be `(User, Option)`, not `(User, Post)` where every field of `Post` is an `Option`. Since we now provide a `.nullable()` method in the core DSL, I think we can simply require calling that method explicitly if you want that tuple conversion to occur. I think that the most common time that conversion will even be used is when the default select clause is used, where we can just handle it for our users automatically. The other reason that we went with a type parameter originally was that it was easier, since we can provide a default value for a type parameter but not an associated type. This turned out to actually be a drawback, as it led to #104. This PR actually brings back aspects of that issue, which I'll get to in a moment. It's expected that any expression which implements `SelectableExpression` have a `T: SelectableExpression` bound for each of its parts. The problem is, the missing second parameter is defaulting to `T::SqlType`, which means we are implicitly saying that this bound only applies for `QS` which does not change the SQL type (anything except a left outer join). This ultimately led to #621. However, with our current structure, it is impossible to fix #621 without re-introducing at least some aspects of #104. In https://github.com/diesel-rs/diesel/issues/104#issuecomment-172281522 I said that we didn't need to worry about `1 + NULL`, because we didn't implement add for any nullable types. However, I'm not sure I considered joins when I made that statement. The statement applied to joins previously because of that implicit "sql type doesn't change" constraint. This commit removes that constraint, meaning #104 will be back at least when the nullability comes from being on the right side of a left join. I don't think this is a serious enough issue that we need to immediately address it, as the types of queries which would cause the issue still just don't happen in practice. We should come up with a long term plan for it, though. Ultimately the nullability of a field really only matters in the select clause. Since any operation on null returns null, and you basically want null to act as false in the where clasue, it doesn't matter there. So one partial step we could take is to break this out into two separate traits. One for the "make sure this is valid given the from clause", and one for the "make this nullable sometimes" case and only constrain on the first one in the where clause. We could then re-add the "sql type doesn't change" constraint on the problem cases, which will bring back aspects of #621, but only for select clauses which is a smaller problem. I'm not sure if I ultimately want to go the two traits route or not. If nothing else, the problem cases are much more obvious with this commit. Anywhere that has `type SqlTypeForSelect = Self::SqlType` is likely a problem case when joins are involved. This will make it easier to find all the places to apply a solution when I come up with one that I'm happy with. Fixes #621. --- CHANGELOG.md | 6 ++ diesel/src/expression/aliased.rs | 5 +- diesel/src/expression/array_comparison.rs | 11 ++- diesel/src/expression/bound.rs | 1 + diesel/src/expression/coerce.rs | 1 + diesel/src/expression/count.rs | 2 + diesel/src/expression/exists.rs | 1 + .../global_expression_methods.rs | 2 +- .../expression/functions/aggregate_folding.rs | 1 + .../functions/aggregate_ordering.rs | 1 + .../src/expression/functions/date_and_time.rs | 1 + diesel/src/expression/functions/mod.rs | 2 + diesel/src/expression/grouped.rs | 1 + diesel/src/expression/mod.rs | 33 ++++----- diesel/src/expression/nullable.rs | 4 ++ diesel/src/expression/ops/numeric.rs | 1 + diesel/src/expression/predicates.rs | 2 + diesel/src/expression/sql_literal.rs | 1 + diesel/src/macros/mod.rs | 34 +++++---- diesel/src/pg/expression/array_comparison.rs | 2 + diesel/src/pg/expression/date_and_time.rs | 6 +- diesel/src/query_builder/functions.rs | 2 +- .../query_builder/select_statement/boxed.rs | 10 +-- .../select_statement/dsl_impls.rs | 72 +++++++++---------- .../src/query_builder/select_statement/mod.rs | 60 ++++++++-------- diesel/src/query_source/joins.rs | 25 ++++--- diesel/src/types/impls/tuples.rs | 16 +---- ...mix_aggregate_and_non_aggregate_selects.rs | 7 -- .../find_requires_correct_type.rs | 1 - .../select_requires_column_from_same_table.rs | 6 -- ...uires_all_must_be_from_selectable_table.rs | 11 --- diesel_infer_schema/src/information_schema.rs | 2 +- diesel_tests/tests/expressions/mod.rs | 4 +- diesel_tests/tests/filter.rs | 2 +- diesel_tests/tests/joins.rs | 28 ++++++-- diesel_tests/tests/types.rs | 2 +- diesel_tests/tests/types_roundtrip.rs | 2 +- 37 files changed, 199 insertions(+), 169 deletions(-) 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 7cf24130bdd8..bd5a825cba21 100644 --- a/diesel/src/expression/functions/aggregate_folding.rs +++ b/diesel/src/expression/functions/aggregate_folding.rs @@ -55,6 +55,7 @@ macro_rules! fold_function { $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 c2f9507005ae..19d05466d482 100644 --- a/diesel/src/expression/functions/aggregate_ordering.rs +++ b/diesel/src/expression/functions/aggregate_ordering.rs @@ -54,6 +54,7 @@ macro_rules! ord_function { $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 7bd541a1e3a3..728824e4cab2 100644 --- a/diesel/src/pg/expression/array_comparison.rs +++ b/diesel/src/pg/expression/array_comparison.rs @@ -143,6 +143,7 @@ impl SelectableExpression for Any where Any: Expression, Expr: SelectableExpression, { + type SqlTypeForSelect = Self::SqlType; } impl NonAggregate for Any where @@ -216,6 +217,7 @@ impl SelectableExpression for All where All: Expression, Expr: SelectableExpression, { + type SqlTypeForSelect = Self::SqlType; } impl NonAggregate for All where 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/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()));