Skip to content

Commit

Permalink
Add (very rudimentary) support for the GROUP BY clause
Browse files Browse the repository at this point in the history
This is not part of the public API, and will almost certainly have
breaking changes made to it when we actually properly support this. This
commit only exists to give a more sane workaround for #210.
  • Loading branch information
sgrif committed Feb 13, 2016
1 parent 340212d commit 6df20de
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 44 deletions.
1 change: 1 addition & 0 deletions diesel/src/query_builder/group_by_clause.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
simple_clause!(NoGroupByClause, GroupByClause, " GROUP BY ");
1 change: 1 addition & 0 deletions diesel/src/query_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod functions;
pub mod nodes;
#[macro_use]
mod clause_macro;
mod group_by_clause;
mod limit_clause;
mod offset_clause;
mod order_clause;
Expand Down
71 changes: 43 additions & 28 deletions diesel/src/query_builder/select_statement/dsl_impls.rs
Original file line number Diff line number Diff line change
@@ -1,93 +1,108 @@
use expression::*;
use expression::aliased::Aliased;
use query_builder::{Query, SelectStatement};
use query_builder::group_by_clause::*;
use query_builder::limit_clause::*;
use query_builder::offset_clause::*;
use query_builder::order_clause::*;
use query_builder::where_clause::*;
use query_dsl::*;
use types::{self, Bool};

impl<ST, S, F, W, O, L, Of, Selection, Type> SelectDsl<Selection, Type>
for SelectStatement<ST, S, F, W, O, L, Of> where
impl<ST, S, F, W, O, L, Of, G, Selection, Type> SelectDsl<Selection, Type>
for SelectStatement<ST, S, F, W, O, L, Of, G> where
Selection: Expression,
SelectStatement<Type, Selection, F, W, O, L, Of>: Query<SqlType=Type>,
SelectStatement<Type, Selection, F, W, O, L, Of, G>: Query<SqlType=Type>,
{
type Output = SelectStatement<Type, Selection, F, W, O, L, Of>;
type Output = SelectStatement<Type, Selection, F, W, O, L, Of, G>;

fn select(self, selection: Selection) -> Self::Output {
SelectStatement::new(selection, self.from, self.where_clause, self.order,
self.limit, self.offset)
self.limit, self.offset, self.group_by)
}
}

impl<ST, S, F, W, O, L, Of, Predicate> FilterDsl<Predicate>
for SelectStatement<ST, S, F, W, O, L, Of> where
impl<ST, S, F, W, O, L, Of, G, Predicate> FilterDsl<Predicate>
for SelectStatement<ST, S, F, W, O, L, Of, G> where
Predicate: SelectableExpression<F, SqlType=Bool> + NonAggregate,
W: WhereAnd<Predicate>,
SelectStatement<ST, S, F, W::Output, O, L, Of>: Query,
SelectStatement<ST, S, F, W::Output, O, L, Of, G>: Query,
{
type Output = SelectStatement<ST, S, F, W::Output, O, L, Of>;
type Output = SelectStatement<ST, S, F, W::Output, O, L, Of, G>;

fn filter(self, predicate: Predicate) -> Self::Output {
SelectStatement::new(self.select, self.from, self.where_clause.and(predicate),
self.order, self.limit, self.offset)
self.order, self.limit, self.offset, self.group_by)
}
}

impl<ST, S, F, W, O, L, Of, Expr> OrderDsl<Expr>
for SelectStatement<ST, S, F, W, O, L, Of> where
impl<ST, S, F, W, O, L, Of, G, Expr> OrderDsl<Expr>
for SelectStatement<ST, S, F, W, O, L, Of, G> where
Expr: SelectableExpression<F>,
SelectStatement<ST, S, F, W, OrderClause<Expr>, L, Of>: Query<SqlType=ST>,
SelectStatement<ST, S, F, W, OrderClause<Expr>, L, Of, G>: Query<SqlType=ST>,
{
type Output = SelectStatement<ST, S, F, W, OrderClause<Expr>, L, Of>;
type Output = SelectStatement<ST, S, F, W, OrderClause<Expr>, L, Of, G>;

fn order(self, expr: Expr) -> Self::Output {
let order = OrderClause(expr);
SelectStatement::new(self.select, self.from, self.where_clause, order,
self.limit, self.offset)
self.limit, self.offset, self.group_by)
}
}

#[doc(hidden)]
pub type Limit = <i64 as AsExpression<types::BigInt>>::Expression;

impl<ST, S, F, W, O, L, Of> LimitDsl for SelectStatement<ST, S, F, W, O, L, Of> where
SelectStatement<ST, S, F, W, O, LimitClause<Limit>, Of>: Query<SqlType=ST>,
impl<ST, S, F, W, O, L, Of, G> LimitDsl for SelectStatement<ST, S, F, W, O, L, Of, G> where
SelectStatement<ST, S, F, W, O, LimitClause<Limit>, Of, G>: Query<SqlType=ST>,
{
type Output = SelectStatement<ST, S, F, W, O, LimitClause<Limit>, Of>;
type Output = SelectStatement<ST, S, F, W, O, LimitClause<Limit>, Of, G>;

fn limit(self, limit: i64) -> Self::Output {
let limit_clause = LimitClause(AsExpression::<types::BigInt>::as_expression(limit));
SelectStatement::new(self.select, self.from, self.where_clause,
self.order, limit_clause, self.offset)
self.order, limit_clause, self.offset, self.group_by)
}
}

#[doc(hidden)]
pub type Offset = Limit;

impl<ST, S, F, W, O, L, Of> OffsetDsl for SelectStatement<ST, S, F, W, O, L, Of> where
SelectStatement<ST, S, F, W, O, L, OffsetClause<Offset>>: Query<SqlType=ST>,
impl<ST, S, F, W, O, L, Of, G> OffsetDsl for SelectStatement<ST, S, F, W, O, L, Of, G> where
SelectStatement<ST, S, F, W, O, L, OffsetClause<Offset>, G>: Query<SqlType=ST>,
{
type Output = SelectStatement<ST, S, F, W, O, L, OffsetClause<Offset>>;
type Output = SelectStatement<ST, S, F, W, O, L, OffsetClause<Offset>, G>;

fn offset(self, offset: i64) -> Self::Output {
let offset_clause = OffsetClause(AsExpression::<types::BigInt>::as_expression(offset));
SelectStatement::new(self.select, self.from, self.where_clause,
self.order, self.limit, offset_clause)
self.order, self.limit, offset_clause, self.group_by)
}
}

impl<'a, ST, S, F, W, O, L, Of, Expr> WithDsl<'a, Expr>
for SelectStatement<ST, S, F, W, O, L, Of> where
SelectStatement<ST, S, WithQuerySource<'a, F, Expr>, W, O, L, Of>: Query,
impl<'a, ST, S, F, W, O, L, Of, G, Expr> WithDsl<'a, Expr>
for SelectStatement<ST, S, F, W, O, L, Of, G> where
SelectStatement<ST, S, WithQuerySource<'a, F, Expr>, W, O, L, Of, G>: Query,
{
type Output = SelectStatement<ST, S, WithQuerySource<'a, F, Expr>, W, O, L, Of>;
type Output = SelectStatement<ST, S, WithQuerySource<'a, F, Expr>, W, O, L, Of, G>;

fn with(self, expr: Aliased<'a, Expr>) -> Self::Output {
let source = WithQuerySource::new(self.from, expr);
SelectStatement::new(self.select, source, self.where_clause,
self.order, self.limit, self.offset)
self.order, self.limit, self.offset, self.group_by)
}
}

impl<ST, S, F, W, O, L, Of, G, Expr> GroupByDsl<Expr>
for SelectStatement<ST, S, F, W, O, L, Of, G> where
SelectStatement<ST, S, F, W, O, L, Of, GroupByClause<Expr>>: Query,
Expr: Expression,
{
type Output = SelectStatement<ST, S, F, W, O, L, Of, GroupByClause<Expr>>;

fn group_by(self, expr: Expr) -> Self::Output {
let group_by = GroupByClause(expr);
SelectStatement::new(self.select, self.from, self.where_clause,
self.order, self.limit, self.offset, group_by)
}
}
56 changes: 40 additions & 16 deletions diesel/src/query_builder/select_statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use expression::*;
use query_source::*;
use std::marker::PhantomData;
use super::{Query, QueryBuilder, QueryFragment, BuildQueryResult};
use super::group_by_clause::NoGroupByClause;
use super::limit_clause::NoLimitClause;
use super::offset_clause::NoOffsetClause;
use super::order_clause::NoOrderClause;
Expand All @@ -20,75 +21,94 @@ pub struct SelectStatement<
Order = NoOrderClause,
Limit = NoLimitClause,
Offset = NoOffsetClause,
GroupBy = NoGroupByClause,
> {
select: Select,
from: From,
where_clause: Where,
order: Order,
limit: Limit,
offset: Offset,
group_by: GroupBy,
_marker: PhantomData<SqlType>,
}

impl<ST, S, F, W, O, L, Of> SelectStatement<ST, S, F, W, O, L, Of> {
pub fn new(select: S, from: F, where_clause: W, order: O, limit: L, offset: Of) -> Self {
impl<ST, S, F, W, O, L, Of, G> SelectStatement<ST, S, F, W, O, L, Of, G> {
pub fn new(
select: S,
from: F,
where_clause: W,
order: O,
limit: L,
offset: Of,
group_by: G,
) -> Self {
SelectStatement {
select: select,
from: from,
where_clause: where_clause,
order: order,
limit: limit,
offset: offset,
group_by: group_by,
_marker: PhantomData,
}
}

pub fn inner_join<T>(self, other: T)
-> SelectStatement<ST, S, InnerJoinSource<F, T>, W, O, L, Of> where
-> SelectStatement<ST, S, InnerJoinSource<F, T>, W, O, L, Of, G> where
T: Table,
F: Table + JoinTo<T, joins::Inner>,
{
SelectStatement::new(self.select, self.from.inner_join(other),
self.where_clause, self.order, self.limit, self.offset)
self.where_clause, self.order, self.limit, self.offset, self.group_by)
}

pub fn left_outer_join<T>(self, other: T)
-> SelectStatement<ST, S, LeftOuterJoinSource<F, T>, W, O, L, Of> where
-> SelectStatement<ST, S, LeftOuterJoinSource<F, T>, W, O, L, Of, G> where
T: Table,
F: Table + JoinTo<T, joins::LeftOuter>,
{
SelectStatement::new(self.select, self.from.left_outer_join(other),
self.where_clause, self.order, self.limit, self.offset)
self.where_clause, self.order, self.limit, self.offset, self.group_by)
}
}

impl<ST, S, F> SelectStatement<ST, S, F> {
pub fn simple(select: S, from: F) -> Self {
SelectStatement::new(select, from, NoWhereClause, NoOrderClause, NoLimitClause, NoOffsetClause)
SelectStatement::new(
select,
from,
NoWhereClause,
NoOrderClause,
NoLimitClause,
NoOffsetClause,
NoGroupByClause,
)
}
}

impl<ST, S, F, W, O, L, Of> Query for SelectStatement<ST, S, F, W, O, L, Of> where
impl<ST, S, F, W, O, L, Of, G> Query for SelectStatement<ST, S, F, W, O, L, Of, G> where
S: SelectableExpression<F, ST>,
{
type SqlType = ST;
}

#[cfg(feature = "postgres")]
impl<ST, S, F, W, O, L, Of> Expression for SelectStatement<ST, S, F, W, O, L, Of> where
impl<ST, S, F, W, O, L, Of, G> Expression for SelectStatement<ST, S, F, W, O, L, Of, G> where
S: SelectableExpression<F, ST>,
{
type SqlType = ::types::Array<ST>;
}

#[cfg(not(feature = "postgres"))]
impl<ST, S, F, W, O, L, Of> Expression for SelectStatement<ST, S, F, W, O, L, Of> where
impl<ST, S, F, W, O, L, Of, G> Expression for SelectStatement<ST, S, F, W, O, L, Of, G> where
S: SelectableExpression<F, ST>,
{
type SqlType = ST;
}

impl<ST, S, F, W, O, L, Of, DB> QueryFragment<DB> for SelectStatement<ST, S, F, W, O, L, Of> where
impl<ST, S, F, W, O, L, Of, DB, G> QueryFragment<DB> for SelectStatement<ST, S, F, W, O, L, Of, G> where
DB: Backend,
S: QueryFragment<DB>,
F: QuerySource,
Expand All @@ -97,45 +117,49 @@ impl<ST, S, F, W, O, L, Of, DB> QueryFragment<DB> for SelectStatement<ST, S, F,
O: QueryFragment<DB>,
L: QueryFragment<DB>,
Of: QueryFragment<DB>,
G: QueryFragment<DB>,
{
fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult {
out.push_sql("SELECT ");
try!(self.select.to_sql(out));
out.push_sql(" FROM ");
try!(self.from.from_clause().to_sql(out));
try!(self.where_clause.to_sql(out));
try!(self.group_by.to_sql(out));
try!(self.order.to_sql(out));
try!(self.limit.to_sql(out));
try!(self.offset.to_sql(out));
Ok(())
}
}

impl<ST, S, W, O, L, Of, DB> QueryFragment<DB> for SelectStatement<ST, S, (), W, O, L, Of> where
impl<ST, S, W, O, L, Of, DB, G> QueryFragment<DB> for SelectStatement<ST, S, (), W, O, L, Of, G> where
DB: Backend,
S: QueryFragment<DB>,
W: QueryFragment<DB>,
O: QueryFragment<DB>,
L: QueryFragment<DB>,
Of: QueryFragment<DB>,
G: QueryFragment<DB>,
{
fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult {
out.push_sql("SELECT ");
try!(self.select.to_sql(out));
try!(self.where_clause.to_sql(out));
try!(self.group_by.to_sql(out));
try!(self.order.to_sql(out));
try!(self.limit.to_sql(out));
try!(self.offset.to_sql(out));
Ok(())
}
}

impl<ST, S, F, W, O, L, Of, QS> SelectableExpression<QS> for SelectStatement<ST, S, F, W, O, L, Of> where
SelectStatement<ST, S, F, W, O, L, Of>: Expression,
impl<ST, S, F, W, O, L, Of, QS, G> SelectableExpression<QS> for SelectStatement<ST, S, F, W, O, L, Of, G> where
SelectStatement<ST, S, F, W, O, L, Of, G>: Expression,
{
}

impl<ST, S, F, W, O, L, Of> NonAggregate for SelectStatement<ST, S, F, W, O, L, Of> where
SelectStatement<ST, S, F, W, O, L, Of>: Expression,
impl<ST, S, F, W, O, L, Of, G> NonAggregate for SelectStatement<ST, S, F, W, O, L, Of, G> where
SelectStatement<ST, S, F, W, O, L, Of, G>: Expression,
{
}
21 changes: 21 additions & 0 deletions diesel/src/query_dsl/group_by_dsl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use expression::Expression;
use query_builder::{Query, AsQuery};
use query_source::QuerySource;

pub trait GroupByDsl<Expr: Expression> {
type Output: Query;

fn group_by(self, expr: Expr) -> Self::Output;
}

impl<T, Expr> GroupByDsl<Expr> for T where
Expr: Expression,
T: QuerySource + AsQuery,
T::Query: GroupByDsl<Expr>,
{
type Output = <T::Query as GroupByDsl<Expr>>::Output;

fn group_by(self, expr: Expr) -> Self::Output {
self.as_query().group_by(expr)
}
}
3 changes: 3 additions & 0 deletions diesel/src/query_dsl/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod belonging_to_dsl;
mod count_dsl;
mod group_by_dsl;
#[doc(hidden)]
pub mod limit_dsl;
#[doc(hidden)]
Expand All @@ -16,6 +17,8 @@ mod with_dsl;
pub use self::belonging_to_dsl::BelongingToDsl;
pub use self::count_dsl::CountDsl;
pub use self::filter_dsl::{FilterDsl, FindDsl};
#[doc(hidden)]
pub use self::group_by_dsl::GroupByDsl;
pub use self::limit_dsl::LimitDsl;
pub use self::load_dsl::{LoadDsl, ExecuteDsl};
pub use self::offset_dsl::OffsetDsl;
Expand Down
15 changes: 15 additions & 0 deletions diesel_tests/tests/group_by.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use schema::*;
use diesel::*;

#[test]
// This test is a shim for a feature which is not sufficiently implemented. It
// has been added as we have a user who needs a reasonable workaround, but this
// functionality will change and this test is allowed to change post-1.0
fn group_by_generates_group_by_sql() {
let source = users::table.group_by(users::name).select(users::id).filter(users::hair_color.is_null());
let expected_sql = "SELECT `users`.`id` FROM `users` \
WHERE `users`.`hair_color` IS NULL \
GROUP BY `users`.`name`";

assert_eq!(expected_sql, &debug_sql!(source));
}
1 change: 1 addition & 0 deletions diesel_tests/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod expressions;
mod filter;
mod filter_operators;
mod find;
mod group_by;
mod internal_details;
mod joins;
mod macros;
Expand Down

0 comments on commit 6df20de

Please sign in to comment.