Skip to content

Commit 5fe68e0

Browse files
committed
Implement FilterDsl for BoxedSelectStatement
We had to switch the type of the where clause to an option, and handle whether or not to put `WHERE` in the query explicitly to handle the `AND` case when filter is called twice. I had tried to instead box up `WhereAnd`, but ultimately if you try and write `impl WhereAnd<Box<QueryFragment<DB>>>`, the output type ends up being infinitely recursive, as the output type would be a `Box<WhereAnd>`, and would need the output type specified, etc.
1 parent b55e37f commit 5fe68e0

5 files changed

Lines changed: 85 additions & 7 deletions

File tree

diesel/src/query_builder/select_statement/boxed.rs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
use std::marker::PhantomData;
22

33
use backend::Backend;
4-
use expression::SelectableExpression;
4+
use expression::{SelectableExpression, NonAggregate};
55
use query_builder::*;
66
use query_dsl::*;
77
use query_source::QuerySource;
8-
use types::HasSqlType;
8+
use types::{HasSqlType, Bool};
99

1010
pub struct BoxedSelectStatement<ST, QS, DB> {
1111
select: Box<QueryFragment<DB>>,
1212
from: QS,
13-
where_clause: Box<QueryFragment<DB>>,
13+
where_clause: Option<Box<QueryFragment<DB>>>,
1414
order: Box<QueryFragment<DB>>,
1515
limit: Box<QueryFragment<DB>>,
1616
offset: Box<QueryFragment<DB>>,
@@ -21,7 +21,7 @@ impl<ST, QS, DB> BoxedSelectStatement<ST, QS, DB> {
2121
pub fn new(
2222
select: Box<QueryFragment<DB>>,
2323
from: QS,
24-
where_clause: Box<QueryFragment<DB>>,
24+
where_clause: Option<Box<QueryFragment<DB>>>,
2525
order: Box<QueryFragment<DB>>,
2626
limit: Box<QueryFragment<DB>>,
2727
offset: Box<QueryFragment<DB>>,
@@ -55,7 +55,15 @@ impl<ST, QS, DB> QueryFragment<DB> for BoxedSelectStatement<ST, QS, DB> where
5555
try!(self.select.to_sql(out));
5656
out.push_sql(" FROM ");
5757
try!(self.from.from_clause().to_sql(out));
58-
try!(self.where_clause.to_sql(out));
58+
59+
match self.where_clause {
60+
Some(ref where_clause) => {
61+
out.push_sql(" WHERE ");
62+
try!(where_clause.to_sql(out));
63+
}
64+
None => {}
65+
}
66+
5967
try!(self.order.to_sql(out));
6068
try!(self.limit.to_sql(out));
6169
try!(self.offset.to_sql(out));
@@ -81,3 +89,21 @@ impl<ST, QS, DB, Type, Selection> SelectDsl<Selection, Type>
8189
)
8290
}
8391
}
92+
93+
impl<ST, QS, DB, Predicate> FilterDsl<Predicate>
94+
for BoxedSelectStatement<ST, QS, DB> where
95+
DB: Backend + HasSqlType<ST> + 'static,
96+
Predicate: SelectableExpression<QS, SqlType=Bool> + NonAggregate,
97+
Predicate: QueryFragment<DB> + 'static,
98+
{
99+
type Output = Self;
100+
101+
fn filter(mut self, predicate: Predicate) -> Self::Output {
102+
use expression::predicates::And;
103+
self.where_clause = Some(match self.where_clause {
104+
Some(where_clause) => Box::new(And::new(where_clause, predicate)),
105+
None => Box::new(predicate),
106+
});
107+
self
108+
}
109+
}

diesel/src/query_builder/select_statement/dsl_impls.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ impl<ST, S, F, W, O, L, Of, G, DB> InternalBoxedDsl<DB>
114114
for SelectStatement<ST, S, F, W, O, L, Of, G> where
115115
DB: Backend,
116116
S: QueryFragment<DB> + 'static,
117-
W: QueryFragment<DB> + 'static,
117+
W: Into<Option<Box<QueryFragment<DB>>>>,
118118
O: QueryFragment<DB> + 'static,
119119
L: QueryFragment<DB> + 'static,
120120
Of: QueryFragment<DB> + 'static,
@@ -125,7 +125,7 @@ for SelectStatement<ST, S, F, W, O, L, Of, G> where
125125
BoxedSelectStatement::new(
126126
Box::new(self.select),
127127
self.from,
128-
Box::new(self.where_clause),
128+
self.where_clause.into(),
129129
Box::new(self.order),
130130
Box::new(self.limit),
131131
Box::new(self.offset),

diesel/src/query_builder/where_clause.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ impl<Predicate> WhereAnd<Predicate> for NoWhereClause where
3030
}
3131
}
3232

33+
impl<DB: Backend> Into<Option<Box<QueryFragment<DB>>>> for NoWhereClause {
34+
fn into(self) -> Option<Box<QueryFragment<DB>>> {
35+
None
36+
}
37+
}
38+
3339
#[derive(Debug, Clone, Copy)]
3440
pub struct WhereClause<Expr>(Expr);
3541

@@ -53,3 +59,12 @@ impl<Expr, Predicate> WhereAnd<Predicate> for WhereClause<Expr> where
5359
WhereClause(self.0.and(predicate))
5460
}
5561
}
62+
63+
impl<DB, Predicate> Into<Option<Box<QueryFragment<DB>>>> for WhereClause<Predicate> where
64+
DB: Backend,
65+
Predicate: QueryFragment<DB> + 'static,
66+
{
67+
fn into(self) -> Option<Box<QueryFragment<DB>>> {
68+
Some(Box::new(self.0))
69+
}
70+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#[macro_use]
2+
extern crate diesel;
3+
4+
use diesel::*;
5+
use diesel::pg::Pg;
6+
7+
table! {
8+
users {
9+
id -> Integer,
10+
name -> VarChar,
11+
}
12+
}
13+
14+
table! {
15+
posts {
16+
id -> Integer,
17+
title -> VarChar,
18+
}
19+
}
20+
21+
fn main() {
22+
users::table.into_boxed::<Pg>().filter(posts::title.eq("Hello"));
23+
//~^ ERROR SelectableExpression
24+
}

diesel_tests/tests/boxed_queries.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,16 @@ fn boxed_queries_implement_select_dsl() {
6767
.load::<String>(&connection);
6868
assert_eq!(Ok(vec!["Sean".into(), "Tess".into()]), data);
6969
}
70+
71+
#[test]
72+
fn boxed_queries_implement_filter_dsl() {
73+
let connection = connection_with_sean_and_tess_in_users_table();
74+
insert(&NewUser::new("Shane", None)).into(users::table)
75+
.execute(&connection).unwrap();
76+
let data = users::table.into_boxed()
77+
.select(users::name)
78+
.filter(users::name.ne("Sean"))
79+
.filter(users::name.like("S%"))
80+
.load(&connection);
81+
assert_eq!(Ok(vec![String::from("Shane")]), data);
82+
}

0 commit comments

Comments
 (0)