diff --git a/.travis.yml b/.travis.yml index 1cee9998d2e9..a0bbac46a6d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,9 +19,9 @@ before_install: - | if [[ "$BACKEND" == sqlite ]]; then (sudo apt-get update) && - (wget --quiet -c https://www.sqlite.org/2018/sqlite-autoconf-3240000.tar.gz) && - (tar zxf sqlite-autoconf-3240000.tar.gz;) && - (cd sqlite-autoconf-3240000; \ + (wget --quiet -c https://sqlite.org/2020/sqlite-autoconf-3310100.tar.gz) && + (tar zxf sqlite-autoconf-3310100.tar.gz;) && + (cd sqlite-autoconf-3310100; \ CFLAGS="$CFLAGS -O2 -fno-strict-aliasing \ -DSQLITE_DEFAULT_FOREIGN_KEYS=1 \ -DSQLITE_SECURE_DELETE \ @@ -48,7 +48,7 @@ before_install: --enable-dynamic-extensions \ --libdir=/usr/lib/x86_64-linux-gnu \ --libexecdir=/usr/lib/x86_64-linux-gnu/sqlite3) && - (cd sqlite-autoconf-3240000; sudo make; sudo make install) + (cd sqlite-autoconf-3310100; sudo make; sudo make install) fi before_script: - pip install 'travis-cargo<0.2' --user @@ -100,7 +100,7 @@ matrix: script: - (cd diesel_cli && cargo test --no-default-features --features "sqlite-bundled") - rust: 1.40.0 - name: "Minimal supported rust version == 1.40.0" + name: "Minimal supported rust version == 1.40.0" script: - cargo check --all diff --git a/CHANGELOG.md b/CHANGELOG.md index 76d73f13d7a4..cdbf40a9756e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,6 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/ ### Added -* `NonAggregate` can now be derived for simple cases. - * `Connection` and `SimpleConnection` traits are implemented for a broader range of `r2d2::PooledConnection` types when the `r2d2` feature is enabled. @@ -30,10 +28,18 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/ * Added support for SQLite's `UPSERT`. You can use this feature above SQLite version 3.24.0. +* Multiple aggregate expressions can now appear together in the same select + clause. See [the upgrade notes](#2-0-0-upgrade-non-aggregate) for details. + +* `ValidGrouping` has been added to represent whether an expression is valid for + a given group by clause, and whether or not it's aggregate. It replaces the + functionality of `NonAggregate`. See [the upgrade + notes](#2-0-0-upgrade-non-aggregate) for details. + ### Removed * All previously deprecated items have been removed. -* Support for uuid version < 0.7.0 has been removed +* Support for uuid version < 0.7.0 has been removed. ### Changed @@ -63,6 +69,20 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/ * Boxed queries (constructed from `.into_boxed()`) are now `Send`. +* The handling of mixed aggregate values is more robust. Invalid queries such as + `.select(max(id) + other_column)` are now correctly rejected, and valid + queries such as `.select((count_star(), max(other_column)))` are now correctly + accepted. For more details, see [the upgrade notes](#2-0-0-upgrade-non-aggregate). + +* `NonAggregate` is now a trait alias for `ValidGrouping<()>` for expressions + that are not aggregate. On stable this is a normal trait with a blanket impl, + but it should never be implemented directly. With the `unstable` feature, it + will use trait aliases which prevent manual implementations. + + Due to language limitations, we cannot make the new trait alias by itself + represent everything it used to, so in some rare cases code changes may be + required. See [the upgrade notes](#2-0-0-upgrade-non-aggregate) for details. + ### Fixed * Many types were incorrectly considered non-aggregate when they should not @@ -93,6 +113,38 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/ Please use `diesel::upsert` instead. +### Upgrade Notes + +#### Replacement of `NonAggregate` with `ValidGrouping` + + +FIXME: This should probably be on the website, but I wanted to document it in +the PR adding the changes. + +Key points: + +- Rules for aggregation are now correctly enforced. They match the semantics of + PG or MySQL with `ONLY_FULL_GROUP_BY` enabled. + - As before, `sql` is the escape hatch if needed. + - MySQL users can use `ANY_VALUE`, PG users can use `DISTINCT ON`. Also + consider using max/min/etc to get deterministic values. +- Any `impl NonAggregate` must be replaced with `impl ValidGrouping` +- For most code, `T: NonAggregate` should continue to work. Unless you're + getting a compiler error, you most likely don't need to change it. +- The full equivalent of what `T: NonAggregate` used to mean is: + + where + T: ValidGrouping<()>, + T::IsAggregate: MixedGrouping, + is_aggreagte::No: MixedGrouping, + +- With `feature = "unstable"`, `T: NonAggregate` implies the first two bounds, + but not the third. On stable only the first bound is implied. This is a + language limitation. +- `T: NonAggregate` can still be passed everywhere it could before, but `T: + NonAggregate` no longer implies `(OtherType, T): NonAggregate`. + - With `feature = "unstable"`, `(T, OtherType): NonAggregate` is still implied. + [2-0-migration]: FIXME write a migration guide ## [1.4.4] - 2020-03-22 diff --git a/diesel/Cargo.toml b/diesel/Cargo.toml index b979b6074d8c..45679e83976a 100644 --- a/diesel/Cargo.toml +++ b/diesel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "diesel" -version = "1.4.4" +version = "2.0.0" authors = ["Sean Griffin "] license = "MIT OR Apache-2.0" description = "A safe, extensible ORM and Query Builder for PostgreSQL, SQLite, and MySQL" @@ -34,7 +34,7 @@ bitflags = { version = "1.0", optional = true } r2d2 = { version = ">= 0.8, < 0.9", optional = true } [dependencies.diesel_derives] -version = "~1.4.0" +version = "~2.0.0" path = "../diesel_derives" [dev-dependencies] diff --git a/diesel/src/expression/array_comparison.rs b/diesel/src/expression/array_comparison.rs index 7f85d33d9ff2..1eb5d1db0306 100644 --- a/diesel/src/expression/array_comparison.rs +++ b/diesel/src/expression/array_comparison.rs @@ -5,13 +5,13 @@ use crate::query_builder::*; use crate::result::QueryResult; use crate::sql_types::Bool; -#[derive(Debug, Copy, Clone, QueryId, NonAggregate)] +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] pub struct In { left: T, values: U, } -#[derive(Debug, Copy, Clone, QueryId, NonAggregate)] +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] pub struct NotIn { left: T, values: U, @@ -140,7 +140,7 @@ where } } -#[derive(Debug, Clone, NonAggregate)] +#[derive(Debug, Clone, ValidGrouping)] pub struct Many(Vec); impl Expression for Many { diff --git a/diesel/src/expression/bound.rs b/diesel/src/expression/bound.rs index 875128158c98..4dbb605a2450 100644 --- a/diesel/src/expression/bound.rs +++ b/diesel/src/expression/bound.rs @@ -5,7 +5,7 @@ use crate::backend::Backend; use crate::query_builder::*; use crate::result::QueryResult; use crate::serialize::ToSql; -use crate::sql_types::HasSqlType; +use crate::sql_types::{DieselNumericOps, HasSqlType}; #[derive(Debug, Clone, Copy, DieselNumericOps)] pub struct Bound { @@ -47,4 +47,6 @@ impl SelectableExpression for Bound where Bound: Appea impl AppearsOnTable for Bound where Bound: Expression {} -impl NonAggregate for Bound {} +impl ValidGrouping for Bound { + type IsAggregate = is_aggregate::Never; +} diff --git a/diesel/src/expression/coerce.rs b/diesel/src/expression/coerce.rs index 0d83ffaf018a..3328a3f74f84 100644 --- a/diesel/src/expression/coerce.rs +++ b/diesel/src/expression/coerce.rs @@ -4,6 +4,7 @@ use crate::backend::Backend; use crate::expression::*; use crate::query_builder::*; use crate::result::QueryResult; +use crate::sql_types::DieselNumericOps; #[derive(Debug, Copy, Clone, QueryId, DieselNumericOps)] #[doc(hidden)] @@ -53,4 +54,9 @@ where } } -impl NonAggregate for Coerce where T: NonAggregate {} +impl ValidGrouping for Coerce +where + T: ValidGrouping, +{ + type IsAggregate = T::IsAggregate; +} diff --git a/diesel/src/expression/count.rs b/diesel/src/expression/count.rs index 16df07a8aa35..c75f2cbde64d 100644 --- a/diesel/src/expression/count.rs +++ b/diesel/src/expression/count.rs @@ -1,9 +1,9 @@ use super::functions::sql_function; -use super::Expression; +use super::{Expression, ValidGrouping}; use crate::backend::Backend; use crate::query_builder::*; use crate::result::QueryResult; -use crate::sql_types::BigInt; +use crate::sql_types::{BigInt, DieselNumericOps}; sql_function! { /// Creates a SQL `COUNT` expression @@ -54,7 +54,8 @@ pub fn count_star() -> CountStar { CountStar } -#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps)] +#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps, ValidGrouping)] +#[diesel(aggregate)] #[doc(hidden)] pub struct CountStar; diff --git a/diesel/src/expression/exists.rs b/diesel/src/expression/exists.rs index 3bcc5e486620..8b4d83ed54fe 100644 --- a/diesel/src/expression/exists.rs +++ b/diesel/src/expression/exists.rs @@ -1,6 +1,6 @@ use crate::backend::Backend; use crate::expression::subselect::Subselect; -use crate::expression::{AppearsOnTable, Expression, NonAggregate, SelectableExpression}; +use crate::expression::{AppearsOnTable, Expression, SelectableExpression, ValidGrouping}; use crate::query_builder::*; use crate::result::QueryResult; use crate::sql_types::Bool; @@ -42,7 +42,12 @@ where type SqlType = Bool; } -impl NonAggregate for Exists where Subselect: NonAggregate {} +impl ValidGrouping for Exists +where + Subselect: ValidGrouping, +{ + type IsAggregate = as ValidGrouping>::IsAggregate; +} #[cfg(not(feature = "unstable"))] impl QueryFragment for Exists diff --git a/diesel/src/expression/functions/date_and_time.rs b/diesel/src/expression/functions/date_and_time.rs index 907ad444f249..22b62254e53d 100644 --- a/diesel/src/expression/functions/date_and_time.rs +++ b/diesel/src/expression/functions/date_and_time.rs @@ -1,7 +1,7 @@ use crate::backend::Backend; use crate::expression::coerce::Coerce; use crate::expression::functions::sql_function; -use crate::expression::{AsExpression, Expression}; +use crate::expression::{AsExpression, Expression, ValidGrouping}; use crate::query_builder::*; use crate::result::QueryResult; use crate::sql_types::*; @@ -9,7 +9,7 @@ use crate::sql_types::*; /// Represents the SQL `CURRENT_TIMESTAMP` constant. This is equivalent to the /// `NOW()` function on backends that support it. #[allow(non_camel_case_types)] -#[derive(Debug, Copy, Clone, QueryId, NonAggregate)] +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] pub struct now; impl Expression for now { diff --git a/diesel/src/expression/functions/mod.rs b/diesel/src/expression/functions/mod.rs index ba385e5b6168..fe378ac8d387 100644 --- a/diesel/src/expression/functions/mod.rs +++ b/diesel/src/expression/functions/mod.rs @@ -9,7 +9,13 @@ macro_rules! no_arg_sql_function_body_except_to_sql { ($type_name:ident, $return_type:ty, $docs:expr) => { #[allow(non_camel_case_types)] #[doc=$docs] - #[derive(Debug, Clone, Copy, $crate::query_builder::QueryId, $crate::expression::NonAggregate)] + #[derive( + Debug, + Clone, + Copy, + $crate::query_builder::QueryId, + $crate::expression::ValidGrouping + )] pub struct $type_name; impl $crate::expression::Expression for $type_name { diff --git a/diesel/src/expression/grouped.rs b/diesel/src/expression/grouped.rs index 122e9987dac9..bd775821b964 100644 --- a/diesel/src/expression/grouped.rs +++ b/diesel/src/expression/grouped.rs @@ -1,9 +1,10 @@ use crate::backend::Backend; -use crate::expression::Expression; +use crate::expression::{Expression, ValidGrouping}; use crate::query_builder::*; use crate::result::QueryResult; +use crate::sql_types::DieselNumericOps; -#[derive(Debug, Copy, Clone, QueryId, Default, DieselNumericOps, NonAggregate)] +#[derive(Debug, Copy, Clone, QueryId, Default, DieselNumericOps, ValidGrouping)] pub struct Grouped(pub T); impl Expression for Grouped { diff --git a/diesel/src/expression/mod.rs b/diesel/src/expression/mod.rs index a954b7882edc..07d0681221e0 100644 --- a/diesel/src/expression/mod.rs +++ b/diesel/src/expression/mod.rs @@ -243,35 +243,158 @@ where { } -/// Marker trait to indicate that an expression does not include any aggregate -/// functions. +/// Is this expression valid for a given group by clause? /// -/// Used to ensure that aggregate expressions aren't mixed with -/// non-aggregate expressions in a select clause, and that they're never -/// included in a where clause. +/// Implementations of this trait must ensure that aggregate expressions are +/// not mixed with non-aggregate expressions. /// -/// This trait can be [derived](derive.NonAggregate.html); +/// For generic types, you can determine if your sub-expresssions can appear +/// together using the [`MixedAggregates`] trait. /// -/// ```ignore -/// #[derive(NonAggregate)] -/// struct Plus(Lhs, Rhs); +/// `GroupByClause` will be a tuple containing the set of expressions appearing +/// in the `GROUP BY` portion of the query. If there is no `GROUP BY`, it will +/// be `()`. /// -/// // The following impl will be generated: -/// impl NonAggregate for Plus -/// where -/// Lhs: NonAggregate, -/// Rhs: NonAggregate, -/// { -/// } -/// ``` -pub trait NonAggregate {} +/// This trait can be [derived] +/// +/// [derived]: derive.ValidGrouping.html +/// [`MixedAggregates`]: trait.MixedAggregates.html +pub trait ValidGrouping { + /// Is this expression aggregate? + /// + /// This type should always be one of the structs in the [`is_aggregate`] + /// module. See the documentation of those structs for more details. + /// + /// [`is_aggregate`]: is_aggregate/index.html + type IsAggregate; +} + +impl + ?Sized, GB> ValidGrouping for Box { + type IsAggregate = T::IsAggregate; +} + +impl<'a, T: ValidGrouping + ?Sized, GB> ValidGrouping for &'a T { + type IsAggregate = T::IsAggregate; +} #[doc(inline)] -pub use diesel_derives::NonAggregate; +pub use diesel_derives::ValidGrouping; + +/// Can two `IsAggregate` types appear in the same expression? +/// +/// You should never implement this trait. It will eventually become a trait +/// alias. +/// +/// [`is_aggregate::Yes`] and [`is_aggregate::No`] can only appear with +/// themselves or [`is_aggregate::Never`]. [`is_aggregate::Never`] can appear +/// with anything. +/// +/// [`is_aggregate::Yes`]: is_aggregate/struct.Yes.html +/// [`is_aggregate::No`]: is_aggregate/struct.No.html +/// [`is_aggregate::Never`]: is_aggregate/struct.Never.html +pub trait MixedAggregates { + /// What is the resulting `IsAggregate` type? + type Output; +} + +#[allow(missing_debug_implementations, missing_copy_implementations)] +/// Possible values for `ValidGrouping::IsAggregate` +pub mod is_aggregate { + use super::MixedAggregates; + + /// Yes, this expression is aggregate for the given group by clause. + pub struct Yes; + + /// No, this expression is not aggregate with the given group by clause, + /// but it might be aggregate with a different group by clause. + pub struct No; + + /// This expression is never aggregate, and can appear with any other + /// expression, regardless of whether it is aggregate. + /// + /// Examples of this are literals. `1` does not care about aggregation. + /// `foo + 1` is always valid, regardless of whether `foo` appears in the + /// group by clause or not. + pub struct Never; + + impl MixedAggregates for Yes { + type Output = Yes; + } + + impl MixedAggregates for Yes { + type Output = Yes; + } -impl NonAggregate for Box {} + impl MixedAggregates for No { + type Output = No; + } + + impl MixedAggregates for No { + type Output = No; + } -impl<'a, T: NonAggregate + ?Sized> NonAggregate for &'a T {} + impl MixedAggregates for Never { + type Output = T; + } +} + +// Note that these docs are similar to but slightly different than the stable +// docs below. Make sure if you change these that you also change the docs +// below. +/// Trait alias to represent an expression that isn't aggregate by default. +/// +/// This alias represents a type which is not aggregate if there is no group by +/// clause. More specifically, it represents for types which implement +/// [`ValidGrouping<()>`] where `IsAggregate` is [`is_aggregate::No`] or +/// [`is_aggregate::Yes`]. +/// +/// While this trait is a useful stand-in for common cases, `T: NonAggregate` +/// cannot always be used when `T: ValidGrouping<(), IsAggregate = No>` or +/// `T: ValidGrouping<(), IsAggregate = Never>` could be. For that reason, +/// unless you need to abstract over both columns and literals, you should +/// prefer to use [`ValidGrouping<()>`] in your bounds instead. +/// +/// [`ValidGrouping<()>`]: trait.ValidGrouping.html +/// [`is_aggregate::Yes`]: is_aggregate/struct.Yes.html +/// [`is_aggregate::No`]: is_aggregate/struct.No.html +#[cfg(feature = "unstable")] +pub trait NonAggregate = ValidGrouping<()> +where + >::IsAggregate: + MixedAggregates; + +// Note that these docs are similar to but slightly different than the unstable +// docs above. Make sure if you change these that you also change the docs +// above. +/// Trait alias to represent an expression that isn't aggregate by default. +/// +/// This trait should never be implemented directly. It is replaced with a +/// trait alias when the `unstable` feature is enabled. +/// +/// This alias represents a type which is not aggregate if there is no group by +/// clause. More specifically, it represents for types which implement +/// [`ValidGrouping<()>`] where `IsAggregate` is [`is_aggregate::No`] or +/// [`is_aggregate::Yes`]. +/// +/// While this trait is a useful stand-in for common cases, `T: NonAggregate` +/// cannot always be used when `T: ValidGrouping<(), IsAggregate = No>` or +/// `T: ValidGrouping<(), IsAggregate = Never>` could be. For that reason, +/// unless you need to abstract over both columns and literals, you should +/// prefer to use [`ValidGrouping<()>`] in your bounds instead. +/// +/// [`ValidGrouping<()>`]: trait.ValidGrouping.html +/// [`is_aggregate::Yes`]: is_aggregate/struct.Yes.html +/// [`is_aggregate::No`]: is_aggregate/struct.No.html +#[cfg(not(feature = "unstable"))] +pub trait NonAggregate: ValidGrouping<()> {} + +#[cfg(not(feature = "unstable"))] +impl NonAggregate for T +where + T: ValidGrouping<()>, + T::IsAggregate: MixedAggregates, +{ +} use crate::query_builder::{QueryFragment, QueryId}; @@ -333,7 +456,7 @@ where DB: Backend, Self: Expression, Self: SelectableExpression, - Self: NonAggregate, + Self: ValidGrouping<(), IsAggregate = is_aggregate::No>, Self: QueryFragment, { } @@ -343,7 +466,7 @@ where DB: Backend, T: Expression, T: SelectableExpression, - T: NonAggregate, + T: ValidGrouping<(), IsAggregate = is_aggregate::No>, T: QueryFragment, { } diff --git a/diesel/src/expression/nullable.rs b/diesel/src/expression/nullable.rs index a89fb19457d4..70c400cea638 100644 --- a/diesel/src/expression/nullable.rs +++ b/diesel/src/expression/nullable.rs @@ -3,9 +3,9 @@ use crate::expression::*; use crate::query_builder::*; use crate::query_source::joins::ToInnerJoin; use crate::result::QueryResult; -use crate::sql_types::IntoNullable; +use crate::sql_types::{DieselNumericOps, IntoNullable}; -#[derive(Debug, Copy, Clone, DieselNumericOps, NonAggregate)] +#[derive(Debug, Copy, Clone, DieselNumericOps, ValidGrouping)] pub struct Nullable(T); impl Nullable { diff --git a/diesel/src/expression/operators.rs b/diesel/src/expression/operators.rs index 98a952c853fb..b5d9116f21e8 100644 --- a/diesel/src/expression/operators.rs +++ b/diesel/src/expression/operators.rs @@ -67,7 +67,7 @@ macro_rules! __diesel_operator_body { Copy, $crate::query_builder::QueryId, $crate::sql_types::DieselNumericOps, - $crate::expression::NonAggregate + $crate::expression::ValidGrouping )] #[doc(hidden)] pub struct $name<$($ty_param,)+> { @@ -386,9 +386,11 @@ postfix_operator!(Desc, " DESC", ()); prefix_operator!(Not, "NOT "); +use crate::expression::ValidGrouping; use crate::insertable::{ColumnInsertValue, Insertable}; -use crate::query_builder::ValuesClause; +use crate::query_builder::{QueryId, ValuesClause}; use crate::query_source::Column; +use crate::sql_types::DieselNumericOps; impl Insertable for Eq where @@ -413,7 +415,7 @@ where } } -#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps, NonAggregate)] +#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps, ValidGrouping)] #[doc(hidden)] pub struct Concat { pub(crate) left: L, diff --git a/diesel/src/expression/ops/numeric.rs b/diesel/src/expression/ops/numeric.rs index b13d920567c1..8a7b79793197 100644 --- a/diesel/src/expression/ops/numeric.rs +++ b/diesel/src/expression/ops/numeric.rs @@ -1,12 +1,12 @@ use crate::backend::Backend; -use crate::expression::{Expression, NonAggregate}; +use crate::expression::{Expression, ValidGrouping}; use crate::query_builder::*; use crate::result::QueryResult; use crate::sql_types; macro_rules! numeric_operation { ($name:ident, $op:expr) => { - #[derive(Debug, Copy, Clone, QueryId)] + #[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] pub struct $name { lhs: Lhs, rhs: Rhs, @@ -47,15 +47,6 @@ macro_rules! numeric_operation { } impl_selectable_expression!($name); - - impl NonAggregate for $name - where - Lhs: NonAggregate, - Rhs: NonAggregate, - $name: Expression, - { - } - generic_numeric_expr!($name, A, B); }; } diff --git a/diesel/src/expression/sql_literal.rs b/diesel/src/expression/sql_literal.rs index 2fdf89ab2294..bbc3dfee3792 100644 --- a/diesel/src/expression/sql_literal.rs +++ b/diesel/src/expression/sql_literal.rs @@ -5,6 +5,7 @@ use crate::expression::*; use crate::query_builder::*; use crate::query_dsl::RunQueryDsl; use crate::result::QueryResult; +use crate::sql_types::DieselNumericOps; #[derive(Debug, Clone, DieselNumericOps)] #[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."] @@ -184,7 +185,9 @@ impl SelectableExpression for SqlLiteral {} impl AppearsOnTable for SqlLiteral {} -impl NonAggregate for SqlLiteral {} +impl ValidGrouping for SqlLiteral { + type IsAggregate = is_aggregate::Never; +} /// Use literal SQL in the query builder /// @@ -317,7 +320,9 @@ where type SqlType = Q::SqlType; } -impl NonAggregate for UncheckedBind {} +impl ValidGrouping for UncheckedBind { + type IsAggregate = is_aggregate::Never; +} impl SelectableExpression for UncheckedBind where Self: AppearsOnTable diff --git a/diesel/src/expression/subselect.rs b/diesel/src/expression/subselect.rs index f08b087caaa7..754d80df9f1e 100644 --- a/diesel/src/expression/subselect.rs +++ b/diesel/src/expression/subselect.rs @@ -47,7 +47,9 @@ where // FIXME: This probably isn't sound. The subselect can reference columns from // the outer query, and is affected by the `GROUP BY` clause of the outer query // identically to using it outside of a subselect -impl NonAggregate for Subselect {} +impl ValidGrouping for Subselect { + type IsAggregate = is_aggregate::Never; +} impl QueryFragment for Subselect where diff --git a/diesel/src/lib.rs b/diesel/src/lib.rs index d32f38c313c1..98bf80b74e8a 100644 --- a/diesel/src/lib.rs +++ b/diesel/src/lib.rs @@ -93,7 +93,7 @@ //! You can come ask for help at //! [gitter.im/diesel-rs/diesel](https://gitter.im/diesel-rs/diesel) -#![cfg_attr(feature = "unstable", feature(specialization))] +#![cfg_attr(feature = "unstable", feature(specialization, trait_alias))] // Built-in Lints #![deny(warnings)] #![warn( @@ -130,7 +130,6 @@ #[macro_use] extern crate bitflags; extern crate byteorder; -#[macro_use] extern crate diesel_derives; #[macro_use] diff --git a/diesel/src/macros/mod.rs b/diesel/src/macros/mod.rs index 22cec7d46526..652376fd2336 100644 --- a/diesel/src/macros/mod.rs +++ b/diesel/src/macros/mod.rs @@ -85,7 +85,13 @@ macro_rules! __diesel_column { { } - impl $crate::expression::NonAggregate for $column_name {} + impl $crate::expression::ValidGrouping<()> for $column_name { + type IsAggregate = $crate::expression::is_aggregate::No; + } + + impl $crate::expression::ValidGrouping<$column_name> for $column_name { + type IsAggregate = $crate::expression::is_aggregate::Yes; + } impl $crate::query_source::Column for $column_name { type Table = $table; @@ -799,13 +805,20 @@ macro_rules! __diesel_table_impl { $($imports)* #[allow(non_camel_case_types, dead_code)] - #[derive(Debug, Clone, Copy)] + #[derive(Debug, Clone, Copy, $crate::query_builder::QueryId)] /// Represents `table_name.*`, which is sometimes needed for /// efficient count queries. It cannot be used in place of /// `all_columns`, and has a `SqlType` of `()` to prevent it /// being used that way pub struct star; + impl<__GB> $crate::expression::ValidGrouping<__GB> for star + where + ($($column_name,)+): $crate::expression::ValidGrouping<__GB>, + { + type IsAggregate = <($($column_name,)+) as $crate::expression::ValidGrouping<__GB>>::IsAggregate; + } + impl Expression for star { type SqlType = (); } diff --git a/diesel/src/mysql/types/mod.rs b/diesel/src/mysql/types/mod.rs index e7679b4826b8..de593a142198 100644 --- a/diesel/src/mysql/types/mod.rs +++ b/diesel/src/mysql/types/mod.rs @@ -9,6 +9,7 @@ use std::io::Write; use crate::deserialize::{self, FromSql}; use crate::mysql::{Mysql, MysqlTypeMetadata, MysqlValue}; +use crate::query_builder::QueryId; use crate::serialize::{self, IsNull, Output, ToSql}; use crate::sql_types::ops::*; use crate::sql_types::*; diff --git a/diesel/src/pg/expression/array.rs b/diesel/src/pg/expression/array.rs index 8f657caba85b..74a6b5f7a248 100644 --- a/diesel/src/pg/expression/array.rs +++ b/diesel/src/pg/expression/array.rs @@ -1,8 +1,8 @@ use crate::backend::Backend; use crate::expression::{ - AppearsOnTable, AsExpressionList, Expression, NonAggregate, SelectableExpression, + AppearsOnTable, AsExpressionList, Expression, SelectableExpression, ValidGrouping, }; -use crate::query_builder::{AstPass, QueryFragment}; +use crate::query_builder::{AstPass, QueryFragment, QueryId}; use crate::sql_types; use std::marker::PhantomData; @@ -90,9 +90,9 @@ where { } -impl NonAggregate for ArrayLiteral +impl ValidGrouping for ArrayLiteral where - T: NonAggregate, - ArrayLiteral: Expression, + T: ValidGrouping, { + type IsAggregate = T::IsAggregate; } diff --git a/diesel/src/pg/expression/array_comparison.rs b/diesel/src/pg/expression/array_comparison.rs index 89ccfc0df399..0f4ef4d43fa1 100644 --- a/diesel/src/pg/expression/array_comparison.rs +++ b/diesel/src/pg/expression/array_comparison.rs @@ -1,5 +1,5 @@ use crate::expression::subselect::Subselect; -use crate::expression::{AsExpression, Expression}; +use crate::expression::{AsExpression, Expression, ValidGrouping}; use crate::pg::Pg; use crate::query_builder::*; use crate::result::QueryResult; @@ -62,7 +62,7 @@ where } #[doc(hidden)] -#[derive(Debug, Copy, Clone, QueryId, NonAggregate)] +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] pub struct Any { expr: Expr, } @@ -95,7 +95,7 @@ where impl_selectable_expression!(Any); #[doc(hidden)] -#[derive(Debug, Copy, Clone, QueryId, NonAggregate)] +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] pub struct All { expr: Expr, } diff --git a/diesel/src/pg/expression/date_and_time.rs b/diesel/src/pg/expression/date_and_time.rs index 2aaf79c34d7c..f0cfff226b48 100644 --- a/diesel/src/pg/expression/date_and_time.rs +++ b/diesel/src/pg/expression/date_and_time.rs @@ -1,4 +1,4 @@ -use crate::expression::Expression; +use crate::expression::{Expression, ValidGrouping}; use crate::pg::Pg; use crate::query_builder::*; use crate::result::QueryResult; @@ -11,7 +11,7 @@ impl DateTimeLike for Timestamp {} impl DateTimeLike for Timestamptz {} impl DateTimeLike for Nullable {} -#[derive(Debug, Copy, Clone, QueryId, NonAggregate)] +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] pub struct AtTimeZone { timestamp: Ts, timezone: Tz, diff --git a/diesel/src/pg/query_builder/distinct_on.rs b/diesel/src/pg/query_builder/distinct_on.rs index 37d464bc52c0..cf8c010efbce 100644 --- a/diesel/src/pg/query_builder/distinct_on.rs +++ b/diesel/src/pg/query_builder/distinct_on.rs @@ -1,6 +1,6 @@ use crate::expression::SelectableExpression; use crate::pg::Pg; -use crate::query_builder::{AstPass, QueryFragment, SelectQuery, SelectStatement}; +use crate::query_builder::{AstPass, QueryFragment, QueryId, SelectQuery, SelectStatement}; use crate::query_dsl::methods::DistinctOnDsl; use crate::result::QueryResult; diff --git a/diesel/src/pg/types/date_and_time/deprecated_time.rs b/diesel/src/pg/types/date_and_time/deprecated_time.rs index 748a813be71b..f6e372df3cfb 100644 --- a/diesel/src/pg/types/date_and_time/deprecated_time.rs +++ b/diesel/src/pg/types/date_and_time/deprecated_time.rs @@ -4,7 +4,8 @@ use std::io::Write; use self::time::{Duration, Timespec}; -use crate::deserialize::{self, FromSql}; +use crate::deserialize::{self, FromSql, FromSqlRow}; +use crate::expression::AsExpression; use crate::pg::{Pg, PgValue}; use crate::serialize::{self, Output, ToSql}; use crate::sql_types; diff --git a/diesel/src/pg/types/date_and_time/mod.rs b/diesel/src/pg/types/date_and_time/mod.rs index e14da6cb6c5c..360f913c0c79 100644 --- a/diesel/src/pg/types/date_and_time/mod.rs +++ b/diesel/src/pg/types/date_and_time/mod.rs @@ -1,7 +1,8 @@ use std::io::Write; use std::ops::Add; -use crate::deserialize::{self, FromSql}; +use crate::deserialize::{self, FromSql, FromSqlRow}; +use crate::expression::AsExpression; use crate::pg::{Pg, PgValue}; use crate::serialize::{self, IsNull, Output, ToSql}; use crate::sql_types::{self, Date, Interval, Time, Timestamp, Timestamptz}; diff --git a/diesel/src/pg/types/floats/mod.rs b/diesel/src/pg/types/floats/mod.rs index 68433c691373..21915594c900 100644 --- a/diesel/src/pg/types/floats/mod.rs +++ b/diesel/src/pg/types/floats/mod.rs @@ -2,7 +2,8 @@ use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; use std::error::Error; use std::io::prelude::*; -use crate::deserialize::{self, FromSql}; +use crate::deserialize::{self, FromSql, FromSqlRow}; +use crate::expression::AsExpression; use crate::pg::{Pg, PgValue}; use crate::serialize::{self, IsNull, Output, ToSql}; use crate::sql_types; diff --git a/diesel/src/pg/types/json.rs b/diesel/src/pg/types/json.rs index 00f6d602ffd8..1717a682eea1 100644 --- a/diesel/src/pg/types/json.rs +++ b/diesel/src/pg/types/json.rs @@ -12,6 +12,8 @@ use crate::sql_types; #[allow(dead_code)] mod foreign_derives { use super::serde_json; + use crate::deserialize::FromSqlRow; + use crate::expression::AsExpression; use crate::sql_types::{Json, Jsonb}; #[derive(FromSqlRow, AsExpression)] diff --git a/diesel/src/pg/types/mac_addr.rs b/diesel/src/pg/types/mac_addr.rs index 69a9d215a03c..a5a97751066c 100644 --- a/diesel/src/pg/types/mac_addr.rs +++ b/diesel/src/pg/types/mac_addr.rs @@ -9,6 +9,8 @@ use crate::sql_types::MacAddr; #[allow(dead_code)] mod foreign_derives { use super::*; + use crate::deserialize::FromSqlRow; + use crate::expression::AsExpression; #[derive(FromSqlRow, AsExpression)] #[diesel(foreign_derive)] diff --git a/diesel/src/pg/types/mod.rs b/diesel/src/pg/types/mod.rs index 9a3183cbfc14..f946ec32be28 100644 --- a/diesel/src/pg/types/mod.rs +++ b/diesel/src/pg/types/mod.rs @@ -24,6 +24,9 @@ mod uuid; /// /// Note: All types in this module can be accessed through `diesel::sql_types` pub mod sql_types { + use crate::query_builder::QueryId; + use crate::sql_types::SqlType; + /// The `OID` SQL type. This is a PostgreSQL specific type. /// /// ### [`ToSql`] impls diff --git a/diesel/src/pg/types/money.rs b/diesel/src/pg/types/money.rs index bd6d909120bc..8939ce1fda2d 100644 --- a/diesel/src/pg/types/money.rs +++ b/diesel/src/pg/types/money.rs @@ -3,7 +3,8 @@ use std::io::prelude::*; use std::ops::{Add, AddAssign, Sub, SubAssign}; -use crate::deserialize::{self, FromSql}; +use crate::deserialize::{self, FromSql, FromSqlRow}; +use crate::expression::AsExpression; use crate::pg::{Pg, PgValue}; use crate::serialize::{self, Output, ToSql}; use crate::sql_types::{BigInt, Money}; diff --git a/diesel/src/pg/types/network_address.rs b/diesel/src/pg/types/network_address.rs index cf8942b685e4..563f1852fc51 100644 --- a/diesel/src/pg/types/network_address.rs +++ b/diesel/src/pg/types/network_address.rs @@ -24,6 +24,8 @@ const PGSQL_AF_INET6: u8 = AF_INET + 1; #[allow(dead_code)] mod foreign_derives { use super::*; + use crate::deserialize::FromSqlRow; + use crate::expression::AsExpression; #[derive(FromSqlRow, AsExpression)] #[diesel(foreign_derive)] diff --git a/diesel/src/pg/types/record.rs b/diesel/src/pg/types/record.rs index aef8ba727e19..05fd2faab451 100644 --- a/diesel/src/pg/types/record.rs +++ b/diesel/src/pg/types/record.rs @@ -3,9 +3,11 @@ use std::io::Write; use std::num::NonZeroU32; use crate::deserialize::{self, FromSql, FromSqlRow, Queryable}; -use crate::expression::{AppearsOnTable, AsExpression, Expression, SelectableExpression}; +use crate::expression::{ + AppearsOnTable, AsExpression, Expression, SelectableExpression, ValidGrouping, +}; use crate::pg::{Pg, PgValue}; -use crate::query_builder::{AstPass, QueryFragment}; +use crate::query_builder::{AstPass, QueryFragment, QueryId}; use crate::result::QueryResult; use crate::row::Row; use crate::serialize::{self, IsNull, Output, ToSql, WriteTuple}; @@ -134,7 +136,7 @@ macro_rules! tuple_impls { __diesel_for_each_tuple!(tuple_impls); -#[derive(Debug, Clone, Copy, QueryId, NonAggregate)] +#[derive(Debug, Clone, Copy, QueryId, ValidGrouping)] pub struct PgTuple(T); impl QueryFragment for PgTuple diff --git a/diesel/src/pg/types/uuid.rs b/diesel/src/pg/types/uuid.rs index b8b625cabdc2..5e5e86ec6956 100644 --- a/diesel/src/pg/types/uuid.rs +++ b/diesel/src/pg/types/uuid.rs @@ -1,6 +1,8 @@ use std::io::prelude::*; +use crate::deserialize::FromSqlRow; use crate::deserialize::{self, FromSql}; +use crate::expression::AsExpression; use crate::pg::{Pg, PgValue}; use crate::serialize::{self, IsNull, Output, ToSql}; use crate::sql_types::Uuid; diff --git a/diesel/src/query_builder/clause_macro.rs b/diesel/src/query_builder/clause_macro.rs index 22173827b83f..33f1b0a5d6cb 100644 --- a/diesel/src/query_builder/clause_macro.rs +++ b/diesel/src/query_builder/clause_macro.rs @@ -6,6 +6,7 @@ macro_rules! simple_clause { ($no_clause:ident, $clause:ident, $sql:expr, backend_bounds = $($backend_bounds:ident),*) => { use crate::backend::Backend; use crate::result::QueryResult; + use crate::query_builder::QueryId; use super::{QueryFragment, AstPass}; #[derive(Debug, Clone, Copy, QueryId)] diff --git a/diesel/src/query_builder/group_by_clause.rs b/diesel/src/query_builder/group_by_clause.rs index b2cf9362d9f0..9584a5abda34 100644 --- a/diesel/src/query_builder/group_by_clause.rs +++ b/diesel/src/query_builder/group_by_clause.rs @@ -1 +1,13 @@ simple_clause!(NoGroupByClause, GroupByClause, " GROUP BY "); + +pub trait ValidGroupByClause { + type Expressions; +} + +impl ValidGroupByClause for NoGroupByClause { + type Expressions = (); +} + +impl ValidGroupByClause for GroupByClause { + type Expressions = GB; +} diff --git a/diesel/src/query_builder/locking_clause.rs b/diesel/src/query_builder/locking_clause.rs index 446798400a15..8605ac930366 100644 --- a/diesel/src/query_builder/locking_clause.rs +++ b/diesel/src/query_builder/locking_clause.rs @@ -1,5 +1,5 @@ use crate::backend::Backend; -use crate::query_builder::{AstPass, QueryFragment}; +use crate::query_builder::{AstPass, QueryFragment, QueryId}; use crate::result::QueryResult; #[derive(Debug, Clone, Copy, QueryId)] diff --git a/diesel/src/query_builder/select_clause.rs b/diesel/src/query_builder/select_clause.rs index 61f08439d98d..54bff358174d 100644 --- a/diesel/src/query_builder/select_clause.rs +++ b/diesel/src/query_builder/select_clause.rs @@ -9,6 +9,7 @@ pub struct DefaultSelectClause; pub struct SelectClause(pub T); pub trait SelectClauseExpression { + type Selection: SelectableExpression; type SelectClauseSqlType; } @@ -16,6 +17,7 @@ impl SelectClauseExpression for SelectClause where T: SelectableExpression, { + type Selection = T; type SelectClauseSqlType = T::SqlType; } @@ -23,6 +25,7 @@ impl SelectClauseExpression for DefaultSelectClause where QS: QuerySource, { + type Selection = QS::DefaultSelection; type SelectClauseSqlType = ::SqlType; } diff --git a/diesel/src/query_builder/select_statement/dsl_impls.rs b/diesel/src/query_builder/select_statement/dsl_impls.rs index 5e03eb3f9601..7412e56352e2 100644 --- a/diesel/src/query_builder/select_statement/dsl_impls.rs +++ b/diesel/src/query_builder/select_statement/dsl_impls.rs @@ -48,7 +48,8 @@ where impl SelectDsl for SelectStatement where - Selection: SelectableExpression, + G: ValidGroupByClause, + Selection: SelectableExpression + ValidGrouping, SelectStatement, D, W, O, L, Of, G, LC>: SelectQuery, { type Output = SelectStatement, D, W, O, L, Of, G, LC>; @@ -268,7 +269,7 @@ where impl GroupByDsl for SelectStatement where - SelectStatement>: Query, + SelectStatement>: SelectQuery, Expr: Expression, { type Output = SelectStatement>; diff --git a/diesel/src/query_builder/select_statement/mod.rs b/diesel/src/query_builder/select_statement/mod.rs index c4944a11ad66..24692696b039 100644 --- a/diesel/src/query_builder/select_statement/mod.rs +++ b/diesel/src/query_builder/select_statement/mod.rs @@ -17,7 +17,7 @@ mod dsl_impls; pub use self::boxed::BoxedSelectStatement; use super::distinct_clause::NoDistinctClause; -use super::group_by_clause::NoGroupByClause; +use super::group_by_clause::*; use super::limit_clause::NoLimitClause; use super::locking_clause::NoLockingClause; use super::offset_clause::NoOffsetClause; @@ -28,7 +28,7 @@ use super::{AstPass, Query, QueryFragment}; use crate::backend::Backend; use crate::expression::subselect::ValidSubselect; use crate::expression::*; -use crate::query_builder::SelectQuery; +use crate::query_builder::{QueryId, SelectQuery}; use crate::query_source::joins::{AppendSelection, Inner, Join}; use crate::query_source::*; use crate::result::QueryResult; @@ -103,7 +103,9 @@ impl SelectStatement { impl Query for SelectStatement where + G: ValidGroupByClause, S: SelectClauseExpression, + S::Selection: ValidGrouping, W: ValidWhereClause, { type SqlType = S::SelectClauseSqlType; diff --git a/diesel/src/query_dsl/group_by_dsl.rs b/diesel/src/query_dsl/group_by_dsl.rs index 1204288e6dfd..a79c145d606f 100644 --- a/diesel/src/query_dsl/group_by_dsl.rs +++ b/diesel/src/query_dsl/group_by_dsl.rs @@ -1,5 +1,5 @@ use crate::expression::Expression; -use crate::query_builder::{AsQuery, Query}; +use crate::query_builder::AsQuery; use crate::query_source::Table; /// This trait is not yet part of Diesel's public API. It may change in the @@ -15,7 +15,7 @@ use crate::query_source::Table; /// query is an error), you may need to use `sql` for your select clause. pub trait GroupByDsl { /// The type returned by `.group_by` - type Output: Query; + type Output; /// See the trait documentation. fn group_by(self, expr: Expr) -> Self::Output; diff --git a/diesel/src/sql_types/mod.rs b/diesel/src/sql_types/mod.rs index 8f3375e7535e..9cd8e981461d 100644 --- a/diesel/src/sql_types/mod.rs +++ b/diesel/src/sql_types/mod.rs @@ -20,6 +20,8 @@ mod ord; pub use self::fold::Foldable; pub use self::ord::SqlOrd; +use crate::query_builder::QueryId; + /// The boolean SQL type. /// /// On backends without a native boolean type, diff --git a/diesel/src/type_impls/date_and_time.rs b/diesel/src/type_impls/date_and_time.rs index bb5662f7465c..49fc57d4c08a 100644 --- a/diesel/src/type_impls/date_and_time.rs +++ b/diesel/src/type_impls/date_and_time.rs @@ -1,5 +1,7 @@ #![allow(dead_code)] +use crate::deserialize::FromSqlRow; +use crate::expression::AsExpression; use std::time::SystemTime; #[derive(FromSqlRow, AsExpression)] @@ -11,6 +13,8 @@ struct SystemTimeProxy(SystemTime); mod chrono { extern crate chrono; use self::chrono::*; + use crate::deserialize::FromSqlRow; + use crate::expression::AsExpression; use crate::sql_types::{Date, Time, Timestamp}; #[derive(FromSqlRow, AsExpression)] diff --git a/diesel/src/type_impls/decimal.rs b/diesel/src/type_impls/decimal.rs index 130a8e4c6ed3..17170b368f04 100644 --- a/diesel/src/type_impls/decimal.rs +++ b/diesel/src/type_impls/decimal.rs @@ -4,6 +4,8 @@ mod bigdecimal { extern crate bigdecimal; use self::bigdecimal::BigDecimal; + use crate::deserialize::FromSqlRow; + use crate::expression::AsExpression; use crate::sql_types::Numeric; #[derive(FromSqlRow, AsExpression)] diff --git a/diesel/src/type_impls/tuples.rs b/diesel/src/type_impls/tuples.rs index 0f4704518b89..a544c10dca74 100644 --- a/diesel/src/type_impls/tuples.rs +++ b/diesel/src/type_impls/tuples.rs @@ -4,7 +4,7 @@ use crate::associations::BelongsTo; use crate::backend::Backend; use crate::deserialize::{self, FromSqlRow, Queryable, QueryableByName}; use crate::expression::{ - AppearsOnTable, AsExpression, AsExpressionList, Expression, NonAggregate, SelectableExpression, + AppearsOnTable, AsExpression, AsExpressionList, Expression, SelectableExpression, ValidGrouping, }; use crate::insertable::{CanInsertInSingleQuery, InsertValues, Insertable}; use crate::query_builder::*; @@ -70,7 +70,7 @@ macro_rules! tuple_impls { } } - impl<$($T: Expression + NonAggregate),+> Expression for ($($T,)+) { + impl<$($T: Expression),+> Expression for ($($T,)+) { type SqlType = ($(<$T as Expression>::SqlType,)+); } @@ -114,8 +114,11 @@ macro_rules! tuple_impls { const HAS_STATIC_QUERY_ID: bool = $($T::HAS_STATIC_QUERY_ID &&)+ true; } - impl<$($T: Expression + NonAggregate),+> NonAggregate for ($($T,)+) { - } + const _: () = { + #[derive(ValidGrouping)] + #[diesel(foreign_derive)] + struct TupleWrapper<$($T,)*>(($($T,)*)); + }; impl<$($T,)+ Tab> UndecoratedInsertRecord for ($($T,)+) where diff --git a/diesel_cli/Cargo.toml b/diesel_cli/Cargo.toml index 730356194e98..d2d6db8cfbf0 100644 --- a/diesel_cli/Cargo.toml +++ b/diesel_cli/Cargo.toml @@ -29,7 +29,7 @@ barrel = { version = ">= 0.5.0", optional = true, features = ["diesel"] } libsqlite3-sys = { version = ">=0.8.0, <0.18.0", optional = true, features = ["min_sqlite_version_3_7_16"] } [dependencies.diesel] -version = "~1.4.0" +version = "~2.0.0" path = "../diesel" default-features = false diff --git a/diesel_cli/src/infer_schema_internals/information_schema.rs b/diesel_cli/src/infer_schema_internals/information_schema.rs index 9b5a96508569..0ead6748efa3 100644 --- a/diesel_cli/src/infer_schema_internals/information_schema.rs +++ b/diesel_cli/src/infer_schema_internals/information_schema.rs @@ -3,7 +3,7 @@ use std::error::Error; use diesel::backend::Backend; use diesel::deserialize::FromSql; -use diesel::expression::NonAggregate; +use diesel::expression::{is_aggregate, ValidGrouping}; #[cfg(feature = "mysql")] use diesel::mysql::Mysql; #[cfg(feature = "postgres")] @@ -17,7 +17,7 @@ use super::table_data::TableName; pub trait UsesInformationSchema: Backend { type TypeColumn: SelectableExpression - + NonAggregate + + ValidGrouping<(), IsAggregate = is_aggregate::No> + QueryId + QueryFragment; diff --git a/diesel_compile_tests/Cargo.toml b/diesel_compile_tests/Cargo.toml index 251ed10d3955..01bc3b81b1a8 100644 --- a/diesel_compile_tests/Cargo.toml +++ b/diesel_compile_tests/Cargo.toml @@ -6,9 +6,6 @@ authors = ["Sean Griffin "] [workspace] [dependencies] -diesel = { version = "1.4.0", default-features = false, features = ["extras", "sqlite", "postgres", "mysql", "unstable"] } +diesel = { version = "2.0.0", default-features = false, features = ["extras", "sqlite", "postgres", "mysql", "unstable"], path = "../diesel" } compiletest_rs = "=0.4" -[replace] -"diesel:1.4.4" = { path = "../diesel" } -"diesel_derives:1.4.1" = { path = "../diesel_derives" } diff --git a/diesel_compile_tests/tests/compile-fail/cannot_load_default_select_with_group_by.rs b/diesel_compile_tests/tests/compile-fail/cannot_load_default_select_with_group_by.rs new file mode 100644 index 000000000000..5c7bd9a87bba --- /dev/null +++ b/diesel_compile_tests/tests/compile-fail/cannot_load_default_select_with_group_by.rs @@ -0,0 +1,19 @@ +#[macro_use] +extern crate diesel; + +use diesel::*; +use diesel::dsl::count; + +table! { + users { + id -> Integer, + name -> Text, + } +} + +fn main() { + let conn = PgConnection::establish("").unwrap(); + let _ = users::table.group_by(users::name) + .load::<(i32, String)>(&conn); + //~^ ERROR ValidGrouping +} 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 c0b7a6fbed88..360114dacb74 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 @@ -14,5 +14,5 @@ fn main() { use self::users::dsl::*; let source = users.select((id, count(users.star()))); - //~^ ERROR E0277 + //~^ ERROR MixedAggregates } diff --git a/diesel_compile_tests/tests/compile-fail/cannot_pass_aggregate_to_where.rs b/diesel_compile_tests/tests/compile-fail/cannot_pass_aggregate_to_where.rs index 437a0ea104e4..e8f1f917a3b1 100644 --- a/diesel_compile_tests/tests/compile-fail/cannot_pass_aggregate_to_where.rs +++ b/diesel_compile_tests/tests/compile-fail/cannot_pass_aggregate_to_where.rs @@ -14,5 +14,5 @@ fn main() { use self::users::dsl::*; let source = users.filter(count(id).gt(3)); - //~^ ERROR NonAggregate + //~^ ERROR MixedAggregates } diff --git a/diesel_compile_tests/tests/compile-fail/custom_returning_requires_nonaggregate.rs b/diesel_compile_tests/tests/compile-fail/custom_returning_requires_nonaggregate.rs index 01f306d61d98..983d58033f2a 100644 --- a/diesel_compile_tests/tests/compile-fail/custom_returning_requires_nonaggregate.rs +++ b/diesel_compile_tests/tests/compile-fail/custom_returning_requires_nonaggregate.rs @@ -20,11 +20,11 @@ fn main() { use self::users::dsl::*; let stmt = update(users.filter(id.eq(1))).set(name.eq("Bill")).returning(count(id)); - //~^ ERROR NonAggregate + //~^ ERROR MixedAggregates let new_user = NewUser { name: "Foobar".to_string(), }; let stmt = insert_into(users).values(&new_user).returning((name, count(name))); - //~^ ERROR E0277 + //~^ ERROR MixedAggregates } diff --git a/diesel_compile_tests/tests/compile-fail/filter_requires_bool_nonaggregate_expression.rs b/diesel_compile_tests/tests/compile-fail/filter_requires_bool_nonaggregate_expression.rs index fbd14f375643..b262cc08ea0a 100644 --- a/diesel_compile_tests/tests/compile-fail/filter_requires_bool_nonaggregate_expression.rs +++ b/diesel_compile_tests/tests/compile-fail/filter_requires_bool_nonaggregate_expression.rs @@ -16,5 +16,5 @@ fn main() { let _ = users::table.filter(users::name); //~^ ERROR type mismatch resolving `::SqlType == diesel::sql_types::Bool` let _ = users::table.filter(sum(users::id).eq(1)); - //~^ ERROR NonAggregate + //~^ ERROR MixedAggregates } diff --git a/diesel_derives/Cargo.toml b/diesel_derives/Cargo.toml index 351fae34e8f4..e0e606f7cdab 100644 --- a/diesel_derives/Cargo.toml +++ b/diesel_derives/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "diesel_derives" -version = "1.4.1" +version = "2.0.0" authors = ["Sean Griffin "] license = "MIT OR Apache-2.0" description = "You should not use this crate directly, it is internal to Diesel." @@ -19,7 +19,7 @@ cfg-if = "0.1.0" dotenv = "0.10.0" [dev-dependencies.diesel] -version = "~1.4.0" +version = "~2.0.0" path = "../diesel" [lib] diff --git a/diesel_derives/src/lib.rs b/diesel_derives/src/lib.rs index cc051c3105a0..1f6d0f1871bd 100644 --- a/diesel_derives/src/lib.rs +++ b/diesel_derives/src/lib.rs @@ -43,12 +43,12 @@ mod diesel_numeric_ops; mod from_sql_row; mod identifiable; mod insertable; -mod non_aggregate; mod query_id; mod queryable; mod queryable_by_name; mod sql_function; mod sql_type; +mod valid_grouping; use diagnostic_shim::*; @@ -258,14 +258,14 @@ pub fn derive_insertable(input: TokenStream) -> TokenStream { expand_proc_macro(input, insertable::derive) } -/// Implements `NonAggregate` -/// -/// This derive can be used for structs with no type parameters, -/// which are not aggregate, as well as for struct which are -/// `NonAggregate` if all type parameters are `NonAggregate`. +#[doc(hidden)] #[proc_macro_derive(NonAggregate)] pub fn derive_non_aggregate(input: TokenStream) -> TokenStream { - expand_proc_macro(input, non_aggregate::derive) + eprintln!( + "#[derive(NonAggregate)] is deprecated. Please use \ + `#[derive(ValidGrouping)]` instead.)" + ); + expand_proc_macro(input, valid_grouping::derive) } /// Implements `QueryId` @@ -435,6 +435,50 @@ pub fn derive_sql_type(input: TokenStream) -> TokenStream { expand_proc_macro(input, sql_type::derive) } +/// Implements `ValidGrouping` +/// +/// This trait can be automatically derived for structs with no type parameters +/// which are never aggregate, as well as for structs which are `NonAggregate` +/// when all type parameters are `NonAggregate`. For example: +/// +/// ```ignore +/// #[derive(ValidGrouping)] +/// struct LiteralOne; +/// +/// #[derive(ValidGrouping)] +/// struct Plus(Lhs, Rhs); +/// +/// // The following impl will be generated: +/// +/// impl ValidGrouping for LiteralOne { +/// type IsAggregate = is_aggregate::Never; +/// } +/// +/// impl ValidGrouping for Plus +/// where +/// Lhs: ValidGrouping, +/// Rhs: ValidGrouping, +/// Lhs::IsAggregate: MixedAggregates, +/// { +/// type IsAggregate = >::Output; +/// } +/// ``` +/// +/// For types which are always considered aggregate (such as an aggregate +/// function), annotate your struct with `#[diesel(aggregate)]` to set `IsAggregate` +/// explicitly to `is_aggregate::Yes`. +/// +/// # Attributes +/// +/// ## Optional type attributes +/// +/// * `#[diesel(aggregate)]` for cases where the type represents an aggregating +/// SQL expression +#[proc_macro_derive(ValidGrouping, attributes(diesel))] +pub fn derive_valid_grouping(input: TokenStream) -> TokenStream { + expand_proc_macro(input, valid_grouping::derive) +} + /// Declare a sql function for use in your code. /// /// Diesel only provides support for a very small number of SQL functions. diff --git a/diesel_derives/src/non_aggregate.rs b/diesel_derives/src/non_aggregate.rs deleted file mode 100644 index e09b882c5975..000000000000 --- a/diesel_derives/src/non_aggregate.rs +++ /dev/null @@ -1,30 +0,0 @@ -use proc_macro2::*; -use syn; - -use util::*; - -pub fn derive(mut item: syn::DeriveInput) -> Result { - let type_params = item - .generics - .type_params() - .map(|param| param.ident.clone()) - .collect::>(); - for type_param in type_params { - let where_clause = item.generics.make_where_clause(); - where_clause - .predicates - .push(parse_quote!(#type_param: NonAggregate)); - } - - let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); - let struct_name = &item.ident; - - Ok(wrap_in_dummy_mod(quote! { - use diesel::expression::NonAggregate; - - impl #impl_generics NonAggregate for #struct_name #ty_generics - #where_clause - { - } - })) -} diff --git a/diesel_derives/src/sql_function.rs b/diesel_derives/src/sql_function.rs index 2d797df44256..3838d35ea518 100644 --- a/diesel_derives/src/sql_function.rs +++ b/diesel_derives/src/sql_function.rs @@ -66,7 +66,7 @@ pub(crate) fn expand(input: SqlFunctionDecl) -> Result let args_iter = args.iter(); let mut tokens = quote! { use diesel::{self, QueryResult}; - use diesel::expression::{AsExpression, Expression, SelectableExpression, AppearsOnTable}; + use diesel::expression::{AsExpression, Expression, SelectableExpression, AppearsOnTable, ValidGrouping}; use diesel::query_builder::{QueryFragment, AstPass}; use diesel::sql_types::*; use super::*; @@ -123,15 +123,29 @@ pub(crate) fn expand(input: SqlFunctionDecl) -> Result } }; - if !is_aggregate { + if is_aggregate { tokens = quote! { #tokens - impl #impl_generics diesel::expression::NonAggregate + impl #impl_generics_internal ValidGrouping<__DieselInternal> for #fn_name #ty_generics - #where_clause - #(#arg_name: diesel::expression::NonAggregate,)* { + type IsAggregate = diesel::expression::is_aggregate::Yes; + } + } + } else { + tokens = quote! { + #tokens + + #[derive(ValidGrouping)] + pub struct __Derived<#(#arg_name,)*>(#(#arg_name,)*); + + impl #impl_generics_internal ValidGrouping<__DieselInternal> + for #fn_name #ty_generics + where + __Derived<#(#arg_name,)*>: ValidGrouping<__DieselInternal>, + { + type IsAggregate = <__Derived<#(#arg_name,)*> as ValidGrouping<__DieselInternal>>::IsAggregate; } }; diff --git a/diesel_derives/src/valid_grouping.rs b/diesel_derives/src/valid_grouping.rs new file mode 100644 index 000000000000..45ec153d048a --- /dev/null +++ b/diesel_derives/src/valid_grouping.rs @@ -0,0 +1,69 @@ +use proc_macro2::*; +use syn; + +use meta::*; +use util::*; + +pub fn derive(mut item: syn::DeriveInput) -> Result { + let flags = + MetaItem::with_name(&item.attrs, "diesel").unwrap_or_else(|| MetaItem::empty("diesel")); + let struct_ty = ty_for_foreign_derive(&item, &flags)?; + let type_params = item + .generics + .type_params() + .map(|param| param.ident.clone()) + .collect::>(); + for type_param in type_params { + let where_clause = item.generics.make_where_clause(); + where_clause + .predicates + .push(parse_quote!(#type_param: ValidGrouping<__GroupByClause>)); + } + + let is_aggregate = flags.has_flag("aggregate"); + + if is_aggregate { + item.generics.params.push(parse_quote!(__GroupByClause)); + let (impl_generics, _, where_clause) = item.generics.split_for_impl(); + Ok(wrap_in_dummy_mod(quote! { + use diesel::expression::{ValidGrouping, MixedAggregates, is_aggregate}; + + impl #impl_generics ValidGrouping<__GroupByClause> for #struct_ty + #where_clause + { + type IsAggregate = is_aggregate::Yes; + } + })) + } else { + let mut aggregates = item + .generics + .type_params() + .map(|t| parse_quote!(#t::IsAggregate)) + .collect::>() + .into_iter(); + let is_aggregate = aggregates + .next() + .map(|first| { + let where_clause = item.generics.make_where_clause(); + aggregates.fold(first, |left, right| { + where_clause.predicates.push(parse_quote!( + #left: MixedAggregates<#right> + )); + parse_quote!(<#left as MixedAggregates<#right>>::Output) + }) + }) + .unwrap_or_else(|| parse_quote!(is_aggregate::Never)); + item.generics.params.push(parse_quote!(__GroupByClause)); + let (impl_generics, _, where_clause) = item.generics.split_for_impl(); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::expression::{ValidGrouping, MixedAggregates, is_aggregate}; + + impl #impl_generics ValidGrouping<__GroupByClause> for #struct_ty + #where_clause + { + type IsAggregate = #is_aggregate; + } + })) + } +} diff --git a/diesel_migrations/Cargo.toml b/diesel_migrations/Cargo.toml index 4168b0ab9e03..481217d27d58 100644 --- a/diesel_migrations/Cargo.toml +++ b/diesel_migrations/Cargo.toml @@ -20,7 +20,7 @@ dotenv = ">=0.8, <0.11" cfg-if = "0.1.0" [dev-dependencies.diesel] -version = "~1.4.0" +version = "~2.0.0" path = "../diesel" default-features = false diff --git a/diesel_migrations/migrations_internals/Cargo.toml b/diesel_migrations/migrations_internals/Cargo.toml index afdecbc6affc..7f6986accd9d 100644 --- a/diesel_migrations/migrations_internals/Cargo.toml +++ b/diesel_migrations/migrations_internals/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://diesel.rs" barrel = { version = ">= 0.5.0", optional = true, features = ["diesel"] } [dependencies.diesel] -version = "~1.4.0" +version = "~2.0.0" path = "../../diesel" default-features = false diff --git a/diesel_tests/tests/group_by.rs b/diesel_tests/tests/group_by.rs index f4035ac03c0e..668fd14d4dd5 100644 --- a/diesel_tests/tests/group_by.rs +++ b/diesel_tests/tests/group_by.rs @@ -8,9 +8,31 @@ use diesel::*; fn group_by_generates_group_by_sql() { let source = users::table .group_by(users::name) - .select(users::id) + .select(users::name) .filter(users::hair_color.is_null()); - let mut expected_sql = "SELECT `users`.`id` FROM `users` \ + let mut expected_sql = "SELECT `users`.`name` FROM `users` \ + WHERE `users`.`hair_color` IS NULL \ + GROUP BY `users`.`name` \ + -- binds: []" + .to_string(); + if cfg!(feature = "postgres") { + expected_sql = expected_sql.replace('`', "\""); + } + + assert_eq!( + expected_sql, + debug_query::(&source).to_string() + ); +} + +#[test] +fn group_by_mixed_aggregate_column_and_aggregate_function() { + use diesel::dsl::max; + let source = users::table + .group_by(users::name) + .select((max(users::id), users::name)) + .filter(users::hair_color.is_null()); + let mut expected_sql = "SELECT max(`users`.`id`), `users`.`name` FROM `users` \ WHERE `users`.`hair_color` IS NULL \ GROUP BY `users`.`name` \ -- binds: []" @@ -33,9 +55,9 @@ fn boxed_queries_have_group_by_method() { let source = users::table .into_boxed::() .group_by(users::name) - .select(users::id) + .select(users::name) .filter(users::hair_color.is_null()); - let mut expected_sql = "SELECT `users`.`id` FROM `users` \ + let mut expected_sql = "SELECT `users`.`name` FROM `users` \ WHERE `users`.`hair_color` IS NULL \ GROUP BY `users`.`name` \ -- binds: []" diff --git a/diesel_tests/tests/select.rs b/diesel_tests/tests/select.rs index 809ba335d066..65c49ce34207 100644 --- a/diesel_tests/tests/select.rs +++ b/diesel_tests/tests/select.rs @@ -497,3 +497,18 @@ fn select_can_be_called_on_query_that_is_valid_subselect_but_invalid_query() { assert_eq!(Ok(vec![tess]), users_with_post_using_name_as_title); } + +#[test] +fn selecting_multiple_aggregate_expressions_without_group_by() { + use self::users::dsl::*; + use diesel::dsl::{count_star, max}; + + let connection = connection_with_sean_and_tess_in_users_table(); + let (count, max_name) = users + .select((count_star(), max(name))) + .get_result::<(i64, _)>(&connection) + .unwrap(); + + assert_eq!(2, count); + assert_eq!(Some(String::from("Tess")), max_name); +} diff --git a/diesel_tests/tests/types.rs b/diesel_tests/tests/types.rs index 8358aa368c95..9b4303f4d592 100644 --- a/diesel_tests/tests/types.rs +++ b/diesel_tests/tests/types.rs @@ -1229,14 +1229,15 @@ where select(sql::(sql_str)).first(&connection).unwrap() } -use diesel::expression::AsExpression; +use diesel::expression::{is_aggregate, AsExpression, ValidGrouping}; use diesel::query_builder::{QueryFragment, QueryId}; use std::fmt::Debug; fn query_to_sql_equality(sql_str: &str, value: U) -> bool where U: AsExpression + Debug + Clone, - U::Expression: SelectableExpression<(), SqlType = T>, + U::Expression: SelectableExpression<(), SqlType = T> + + ValidGrouping<(), IsAggregate = is_aggregate::Never>, U::Expression: QueryFragment + QueryId, T: QueryId + SingleValue, { diff --git a/diesel_tests/tests/types_roundtrip.rs b/diesel_tests/tests/types_roundtrip.rs index febd36d0a9e0..99e46700d1e8 100644 --- a/diesel_tests/tests/types_roundtrip.rs +++ b/diesel_tests/tests/types_roundtrip.rs @@ -12,7 +12,7 @@ pub use diesel::serialize::ToSql; pub use diesel::sql_types::HasSqlType; pub use diesel::*; -use diesel::expression::AsExpression; +use diesel::expression::{AsExpression, NonAggregate}; use diesel::query_builder::{QueryFragment, QueryId}; #[cfg(feature = "postgres")] use std::collections::Bound; @@ -31,6 +31,7 @@ where + Clone + ::std::fmt::Debug, >::Expression: SelectableExpression<(), SqlType = ST> + + NonAggregate + QueryFragment<::Backend> + QueryId, { diff --git a/examples/mysql/all_about_inserts/Cargo.toml b/examples/mysql/all_about_inserts/Cargo.toml index 720654a2f789..78157b19b94f 100644 --- a/examples/mysql/all_about_inserts/Cargo.toml +++ b/examples/mysql/all_about_inserts/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Sean Griffin "] [dependencies] -diesel = { version = "1.4.0", features = ["mysql", "chrono"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["mysql", "chrono"] } serde = "1.0" serde_derive = "1.0" serde_json = "1.0" diff --git a/examples/mysql/getting_started_step_1/Cargo.toml b/examples/mysql/getting_started_step_1/Cargo.toml index b1ac30b0a7f6..bfa2e14b129a 100644 --- a/examples/mysql/getting_started_step_1/Cargo.toml +++ b/examples/mysql/getting_started_step_1/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" authors = ["Sean Griffin "] [dependencies] -diesel = { version = "1.4.0", features = ["mysql"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["mysql"] } dotenv = "0.10" diff --git a/examples/mysql/getting_started_step_2/Cargo.toml b/examples/mysql/getting_started_step_2/Cargo.toml index ecb26e03a470..26f7e54bdc70 100644 --- a/examples/mysql/getting_started_step_2/Cargo.toml +++ b/examples/mysql/getting_started_step_2/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" authors = ["Sean Griffin "] [dependencies] -diesel = { version = "1.4.0", features = ["mysql"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["mysql"] } dotenv = "0.10" diff --git a/examples/mysql/getting_started_step_3/Cargo.toml b/examples/mysql/getting_started_step_3/Cargo.toml index aeab29f6772e..960a7877b659 100644 --- a/examples/mysql/getting_started_step_3/Cargo.toml +++ b/examples/mysql/getting_started_step_3/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" authors = ["Sean Griffin "] [dependencies] -diesel = { version = "1.4.0", features = ["mysql"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["mysql"] } dotenv = "0.10" diff --git a/examples/postgres/advanced-blog-cli/Cargo.toml b/examples/postgres/advanced-blog-cli/Cargo.toml index 9f0b7ffebaa4..c1e57f9e1e22 100644 --- a/examples/postgres/advanced-blog-cli/Cargo.toml +++ b/examples/postgres/advanced-blog-cli/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Sean Griffin "] [dependencies] bcrypt = "0.1.0" chrono = "0.4.0" -diesel = { version = "1.4.0", features = ["postgres", "chrono"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["postgres", "chrono"] } dotenv = "0.10.0" structopt = "0.1.6" structopt-derive = "0.1.6" @@ -14,5 +14,5 @@ tempfile = "2.2.0" [dev-dependencies] assert_matches = "1.1" -diesel_migrations = { version = "1.4.0", features = ["postgres"] } +diesel_migrations = { version = "1.4.0", features = ["postgres"], path = "../../../diesel_migrations" } lazy_static = "1.0" diff --git a/examples/postgres/all_about_inserts/Cargo.toml b/examples/postgres/all_about_inserts/Cargo.toml index 75327d40698c..ec0ddd757861 100644 --- a/examples/postgres/all_about_inserts/Cargo.toml +++ b/examples/postgres/all_about_inserts/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Sean Griffin "] [dependencies] -diesel = { version = "1.4.0", features = ["postgres"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["postgres"] } serde = "1.0" serde_derive = "1.0" serde_json = "1.0" diff --git a/examples/postgres/all_about_updates/Cargo.toml b/examples/postgres/all_about_updates/Cargo.toml index 2e9d24628340..9abffd29d2e7 100644 --- a/examples/postgres/all_about_updates/Cargo.toml +++ b/examples/postgres/all_about_updates/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" authors = ["Sean Griffin "] [dependencies] -diesel = { version = "1.4.0", features = ["postgres"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["postgres"] } diff --git a/examples/postgres/getting_started_step_1/Cargo.toml b/examples/postgres/getting_started_step_1/Cargo.toml index c02bc5aa680f..e6d60314c640 100644 --- a/examples/postgres/getting_started_step_1/Cargo.toml +++ b/examples/postgres/getting_started_step_1/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" authors = ["Sean Griffin "] [dependencies] -diesel = { version = "1.4.0", features = ["postgres"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["postgres"] } dotenv = "0.10" diff --git a/examples/postgres/getting_started_step_2/Cargo.toml b/examples/postgres/getting_started_step_2/Cargo.toml index 35cfb33f4a5d..c1e740b3c2f6 100644 --- a/examples/postgres/getting_started_step_2/Cargo.toml +++ b/examples/postgres/getting_started_step_2/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" authors = ["Sean Griffin "] [dependencies] -diesel = { version = "1.4.0", features = ["postgres"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["postgres"] } dotenv = "0.10" diff --git a/examples/postgres/getting_started_step_3/Cargo.toml b/examples/postgres/getting_started_step_3/Cargo.toml index 06bc58dabb7d..90c61037897b 100644 --- a/examples/postgres/getting_started_step_3/Cargo.toml +++ b/examples/postgres/getting_started_step_3/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" authors = ["Sean Griffin "] [dependencies] -diesel = { version = "1.4.0", features = ["postgres"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["postgres"] } dotenv = "0.10" diff --git a/examples/sqlite/all_about_inserts/Cargo.toml b/examples/sqlite/all_about_inserts/Cargo.toml index 5d1f2ff6d583..36a2a94d62b4 100644 --- a/examples/sqlite/all_about_inserts/Cargo.toml +++ b/examples/sqlite/all_about_inserts/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Sean Griffin "] [dependencies] -diesel = { version = "1.4.0", features = ["sqlite", "chrono"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["sqlite", "chrono"] } serde = "1.0" serde_derive = "1.0" serde_json = "1.0" diff --git a/examples/sqlite/getting_started_step_1/Cargo.toml b/examples/sqlite/getting_started_step_1/Cargo.toml index a2625281d8d5..60e3da4dbfb1 100644 --- a/examples/sqlite/getting_started_step_1/Cargo.toml +++ b/examples/sqlite/getting_started_step_1/Cargo.toml @@ -5,5 +5,5 @@ license = "MIT OR Apache-2.0" authors = ["Taryn Hill "] [dependencies] -diesel = { version = "1.4.0", features = ["sqlite"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["sqlite"] } dotenv = "0.10" diff --git a/examples/sqlite/getting_started_step_2/Cargo.toml b/examples/sqlite/getting_started_step_2/Cargo.toml index bdd8c3db5e17..4dc805bc2c34 100644 --- a/examples/sqlite/getting_started_step_2/Cargo.toml +++ b/examples/sqlite/getting_started_step_2/Cargo.toml @@ -5,5 +5,5 @@ license = "MIT OR Apache-2.0" authors = ["Taryn Hill "] [dependencies] -diesel = { version = "1.4.0", features = ["sqlite"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["sqlite"] } dotenv = "0.10" diff --git a/examples/sqlite/getting_started_step_3/Cargo.toml b/examples/sqlite/getting_started_step_3/Cargo.toml index 9bcfd0eb7d3c..dd6119aa9613 100644 --- a/examples/sqlite/getting_started_step_3/Cargo.toml +++ b/examples/sqlite/getting_started_step_3/Cargo.toml @@ -5,5 +5,5 @@ license = "MIT OR Apache-2.0" authors = ["Taryn Hill "] [dependencies] -diesel = { version = "1.4.0", features = ["sqlite"] } +diesel = { version = "2.0.0", path = "../../../diesel", features = ["sqlite"] } dotenv = "0.10"