From c66d96fa9cefaba0a418834e8cb0e78a0a87269a Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Thu, 1 Oct 2015 11:40:46 -0400 Subject: [PATCH] Disallow queries which mix aggregate and non-aggregate expressions This query would result in an error on PG (I believe MySQL allows it, but it's a nonsense query). This should only be allowed if the column appears in the group by statement, and I believe I'll be able to reflect that in the type system once I get to that point. --- src/expression/mod.rs | 3 +++ src/query_source/mod.rs | 5 ++++- src/types/impls/tuples.rs | 11 ++++++---- ...mix_aggregate_and_non_aggregate_selects.rs | 20 +++++++++++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 tests/compile-fail/cannot_mix_aggregate_and_non_aggregate_selects.rs diff --git a/src/expression/mod.rs b/src/expression/mod.rs index c27fad8258fa..f3e88fbac9d8 100644 --- a/src/expression/mod.rs +++ b/src/expression/mod.rs @@ -16,3 +16,6 @@ pub trait SelectableExpression< Type: NativeSqlType = ::SqlType, >: Expression { } + +pub trait NonAggregate: Expression { +} diff --git a/src/query_source/mod.rs b/src/query_source/mod.rs index 7077611ebad9..2e3937c46df5 100644 --- a/src/query_source/mod.rs +++ b/src/query_source/mod.rs @@ -1,7 +1,7 @@ mod joins; mod select; -use expression::{Expression, SelectableExpression, count_star}; +use expression::{Expression, SelectableExpression, NonAggregate, count_star}; pub use self::joins::{InnerJoinSource, LeftOuterJoinSource}; use self::select::SelectSqlQuerySource; use std::convert::Into; @@ -67,6 +67,9 @@ impl Expression for C { impl SelectableExpression for C { } +impl NonAggregate for C { +} + pub trait Table: QuerySource { type PrimaryKey: Column; fn name(&self) -> &str; diff --git a/src/types/impls/tuples.rs b/src/types/impls/tuples.rs index db5a54ca42c6..e22e6f9f7a27 100644 --- a/src/types/impls/tuples.rs +++ b/src/types/impls/tuples.rs @@ -1,4 +1,4 @@ -use expression::{Expression, SelectableExpression}; +use expression::{Expression, SelectableExpression, NonAggregate}; use persistable::{AsBindParam, InsertableColumns}; use row::Row; use std::error::Error; @@ -62,7 +62,7 @@ macro_rules! tuple_impls { } } - impl<$($T: Expression),+> Expression for ($($T),+) { + impl<$($T: Expression + NonAggregate),+> Expression for ($($T),+) { type SqlType = ($(<$T as Expression>::SqlType),+); fn to_sql(&self) -> String { @@ -71,6 +71,9 @@ macro_rules! tuple_impls { } } + impl<$($T: Expression + NonAggregate),+> NonAggregate for ($($T),+) { + } + impl<$($T: Column),+, T: Table> InsertableColumns for ($($T),+) { type Table = T; type SqlType = ($(<$T as Column>::SqlType),+); @@ -85,7 +88,7 @@ macro_rules! tuple_impls { SelectableExpression for ($($T),+) where $($ST: NativeSqlType),+, - $($T: SelectableExpression),+, + $($T: SelectableExpression + NonAggregate),+, QS: QuerySource, { } @@ -94,7 +97,7 @@ macro_rules! tuple_impls { SelectableExpression> for ($($T),+) where $($ST: NativeSqlType),+, - $($T: SelectableExpression>),+, + $($T: SelectableExpression> + NonAggregate),+, QS: QuerySource, { } diff --git a/tests/compile-fail/cannot_mix_aggregate_and_non_aggregate_selects.rs b/tests/compile-fail/cannot_mix_aggregate_and_non_aggregate_selects.rs new file mode 100644 index 000000000000..49620748b3a4 --- /dev/null +++ b/tests/compile-fail/cannot_mix_aggregate_and_non_aggregate_selects.rs @@ -0,0 +1,20 @@ +#[macro_use] +extern crate yaqb; + +use yaqb::*; +use yaqb::expression::count; + +table! { + users { + id -> Serial, + } +} + +fn main() { + use self::users::columns::*; + use self::users::table as users; + + let connection = Connection::establish("").unwrap(); + let source = users.select((id, count(star))); + //~^ ERROR E0277 +}