Skip to content

Commit fee5982

Browse files
committed
Simplify implementation
* Add a method on `QueryDsl` to document the new method * Change `Nullable<SelectClause<T>>` into `SelectClause<Nullable<T>>` because this gives a better implementation (Thanks for the idea Sean) * Do not use internal api's in new tests
1 parent 0e22ed0 commit fee5982

10 files changed

Lines changed: 184 additions & 116 deletions

File tree

diesel/src/expression/nullable.rs

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
use backend::Backend;
22
use expression::*;
3-
use query_builder::select_clause::{BoxSelectClause, SelectClauseExpression,
4-
SelectClauseQueryFragment};
53
use query_builder::*;
64
use query_source::Table;
75
use result::QueryResult;
@@ -56,38 +54,10 @@ where
5654
{
5755
}
5856

59-
impl<T, Tab> SelectableExpression<Tab> for Nullable<T>
57+
impl<T, QS> SelectableExpression<QS> for Nullable<T>
6058
where
61-
Self: AppearsOnTable<Tab>,
62-
T: SelectableExpression<Tab>,
63-
Tab: Table,
59+
Self: AppearsOnTable<QS>,
60+
T: SelectableExpression<QS>,
61+
QS: Table,
6462
{
6563
}
66-
67-
impl<T, QS> SelectClauseExpression<QS> for Nullable<T>
68-
where
69-
T: SelectClauseExpression<QS>,
70-
T::SelectClauseSqlType: ::sql_types::NotNull,
71-
{
72-
type SelectClauseSqlType = ::sql_types::Nullable<T::SelectClauseSqlType>;
73-
}
74-
75-
impl<T, QS, DB> SelectClauseQueryFragment<QS, DB> for Nullable<T>
76-
where
77-
T: SelectClauseQueryFragment<QS, DB>,
78-
DB: Backend,
79-
{
80-
fn walk_ast(&self, source: &QS, pass: AstPass<DB>) -> QueryResult<()> {
81-
self.0.walk_ast(source, pass)
82-
}
83-
}
84-
85-
impl<'a, QS, DB, T> BoxSelectClause<'a, QS, DB> for Nullable<T>
86-
where
87-
DB: Backend,
88-
T: BoxSelectClause<'a, QS, DB>,
89-
{
90-
fn box_select_clause(self, qs: &QS) -> Box<QueryFragment<DB> + 'a> {
91-
self.0.box_select_clause(qs)
92-
}
93-
}

diesel/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,9 @@ pub mod helper_types {
316316

317317
/// Represents the return type of `.single_value()`
318318
pub type SingleValue<Source> = <Source as SingleValueDsl>::Output;
319+
320+
/// Represents the return type of `.nullable()`
321+
pub type NullableSelect<Source> = <Source as NullableSelectDsl>::Output;
319322
}
320323

321324
pub mod prelude {

diesel/src/query_builder/mod.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub mod nodes;
2525
mod offset_clause;
2626
mod order_clause;
2727
mod returning_clause;
28-
pub(crate) mod select_clause;
28+
mod select_clause;
2929
mod select_statement;
3030
mod sql_query;
3131
mod update_statement;
@@ -49,9 +49,6 @@ pub use self::update_statement::IncompleteUpdateStatement;
4949
pub use self::update_statement::{AsChangeset, BoxedUpdateStatement, IntoUpdateTarget,
5050
UpdateStatement, UpdateTarget};
5151

52-
#[doc(hidden)]
53-
pub use self::select_clause::BoxSelectClause;
54-
5552
pub(crate) use self::insert_statement::ColumnList;
5653

5754
use std::error::Error;

diesel/src/query_builder/select_clause.rs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -50,29 +50,3 @@ where
5050
source.default_selection().walk_ast(pass)
5151
}
5252
}
53-
54-
#[doc(hidden)]
55-
pub trait BoxSelectClause<'a, QS, DB: Backend> {
56-
fn box_select_clause(self, qs: &QS) -> Box<QueryFragment<DB> + 'a>;
57-
}
58-
59-
impl<'a, QS, DB, T> BoxSelectClause<'a, QS, DB> for SelectClause<T>
60-
where
61-
DB: Backend,
62-
T: QueryFragment<DB> + SelectableExpression<QS> + 'a,
63-
{
64-
fn box_select_clause(self, _: &QS) -> Box<QueryFragment<DB> + 'a> {
65-
Box::new(self.0)
66-
}
67-
}
68-
69-
impl<'a, QS, DB> BoxSelectClause<'a, QS, DB> for DefaultSelectClause
70-
where
71-
DB: Backend,
72-
QS: QuerySource,
73-
QS::DefaultSelection: QueryFragment<DB> + 'a,
74-
{
75-
fn box_select_clause(self, qs: &QS) -> Box<QueryFragment<DB> + 'a> {
76-
Box::new(qs.default_selection())
77-
}
78-
}

diesel/src/query_builder/select_statement/boxed.rs

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -59,25 +59,6 @@ impl<'a, ST, QS, DB> BoxedSelectStatement<'a, ST, QS, DB> {
5959
}
6060
}
6161

62-
impl<'a, ST, QS, DB> BoxedSelectStatement<'a, ST, QS, DB>
63-
where
64-
ST: NotNull,
65-
{
66-
pub fn nullable(self) -> BoxedSelectStatement<'a, Nullable<ST>, QS, DB> {
67-
BoxedSelectStatement {
68-
select: self.select,
69-
from: self.from,
70-
distinct: self.distinct,
71-
where_clause: self.where_clause,
72-
order: self.order,
73-
limit: self.limit,
74-
offset: self.offset,
75-
group_by: self.group_by,
76-
_marker: PhantomData,
77-
}
78-
}
79-
}
80-
8162
impl<'a, ST, QS, DB> Query for BoxedSelectStatement<'a, ST, QS, DB>
8263
where
8364
DB: Backend,
@@ -339,3 +320,24 @@ where
339320
InsertFromSelect::new(self)
340321
}
341322
}
323+
324+
impl<'a, ST, QS, DB> NullableSelectDsl for BoxedSelectStatement<'a, ST, QS, DB>
325+
where
326+
ST: NotNull,
327+
{
328+
type Output = BoxedSelectStatement<'a, Nullable<ST>, QS, DB>;
329+
330+
fn nullable(self) -> Self::Output {
331+
BoxedSelectStatement {
332+
select: self.select,
333+
from: self.from,
334+
distinct: self.distinct,
335+
where_clause: self.where_clause,
336+
order: self.order,
337+
limit: self.limit,
338+
offset: self.offset,
339+
group_by: self.group_by,
340+
_marker: PhantomData,
341+
}
342+
}
343+
}

diesel/src/query_builder/select_statement/dsl_impls.rs

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::BoxedSelectStatement;
22
use associations::HasTable;
33
use backend::Backend;
44
use dsl::AsExprOf;
5+
use expression::nullable::Nullable;
56
use expression::*;
67
use insertable::Insertable;
78
use query_builder::distinct_clause::*;
@@ -19,7 +20,8 @@ use query_dsl::boxed_dsl::BoxedDsl;
1920
use query_dsl::methods::*;
2021
use query_dsl::*;
2122
use query_source::joins::{Join, JoinOn, JoinTo};
22-
use sql_types::{BigInt, Bool};
23+
use query_source::QuerySource;
24+
use sql_types::{BigInt, Bool, NotNull};
2325

2426
impl<F, S, D, W, O, L, Of, G, LC, Rhs, Kind, On> InternalJoinDsl<Rhs, Kind, On>
2527
for SelectStatement<F, S, D, W, O, L, Of, G, LC>
@@ -337,23 +339,52 @@ impl<F, S, D, W, O, L, Of, G, LC, LM, Modifier> ModifyLockDsl<Modifier>
337339
}
338340
}
339341

340-
impl<'a, F, S, D, W, O, L, Of, G, DB> BoxedDsl<'a, DB> for SelectStatement<F, S, D, W, O, L, Of, G>
342+
impl<'a, F, S, D, W, O, L, Of, G, DB> BoxedDsl<'a, DB>
343+
for SelectStatement<F, SelectClause<S>, D, W, O, L, Of, G>
341344
where
342345
Self: AsQuery,
343346
DB: Backend,
344-
S: BoxSelectClause<'a, F, DB> + SelectClauseExpression<F>,
347+
S: QueryFragment<DB> + SelectableExpression<F> + 'a,
345348
D: QueryFragment<DB> + 'a,
346349
W: Into<BoxedWhereClause<'a, DB>>,
347350
O: Into<Option<Box<QueryFragment<DB> + 'a>>>,
348351
L: QueryFragment<DB> + 'a,
349352
Of: QueryFragment<DB> + 'a,
350353
G: QueryFragment<DB> + 'a,
351354
{
352-
type Output = BoxedSelectStatement<'a, S::SelectClauseSqlType, F, DB>;
355+
type Output = BoxedSelectStatement<'a, S::SqlType, F, DB>;
353356

354357
fn internal_into_boxed(self) -> Self::Output {
355358
BoxedSelectStatement::new(
356-
self.select.box_select_clause(&self.from),
359+
Box::new(self.select.0),
360+
self.from,
361+
Box::new(self.distinct),
362+
self.where_clause.into(),
363+
self.order.into(),
364+
Box::new(self.limit),
365+
Box::new(self.offset),
366+
Box::new(self.group_by),
367+
)
368+
}
369+
}
370+
impl<'a, F, D, W, O, L, Of, G, DB> BoxedDsl<'a, DB>
371+
for SelectStatement<F, DefaultSelectClause, D, W, O, L, Of, G>
372+
where
373+
Self: AsQuery,
374+
DB: Backend,
375+
F: QuerySource,
376+
F::DefaultSelection: QueryFragment<DB> + 'a,
377+
D: QueryFragment<DB> + 'a,
378+
W: Into<BoxedWhereClause<'a, DB>>,
379+
O: Into<Option<Box<QueryFragment<DB> + 'a>>>,
380+
L: QueryFragment<DB> + 'a,
381+
Of: QueryFragment<DB> + 'a,
382+
G: QueryFragment<DB> + 'a,
383+
{
384+
type Output = BoxedSelectStatement<'a, <F::DefaultSelection as Expression>::SqlType, F, DB>;
385+
fn internal_into_boxed(self) -> Self::Output {
386+
BoxedSelectStatement::new(
387+
Box::new(self.from.default_selection()),
357388
self.from,
358389
Box::new(self.distinct),
359390
self.where_clause.into(),
@@ -438,3 +469,51 @@ where
438469
InsertFromSelect::new(self)
439470
}
440471
}
472+
473+
impl<'a, F, S, D, W, O, L, Of, G> NullableSelectDsl
474+
for SelectStatement<F, SelectClause<S>, D, W, O, L, Of, G>
475+
where
476+
SelectClause<S>: SelectClauseExpression<F>,
477+
<SelectClause<S> as SelectClauseExpression<F>>::SelectClauseSqlType: NotNull,
478+
{
479+
type Output = SelectStatement<F, SelectClause<Nullable<S>>, D, W, O, L, Of, G>;
480+
481+
fn nullable(self) -> Self::Output {
482+
SelectStatement::new(
483+
SelectClause(Nullable::new(self.select.0)),
484+
self.from,
485+
self.distinct,
486+
self.where_clause,
487+
self.order,
488+
self.limit,
489+
self.offset,
490+
self.group_by,
491+
self.locking,
492+
)
493+
}
494+
}
495+
496+
impl<'a, F, D, W, O, L, Of, G> NullableSelectDsl
497+
for SelectStatement<F, DefaultSelectClause, D, W, O, L, Of, G>
498+
where
499+
F: QuerySource,
500+
DefaultSelectClause: SelectClauseExpression<F>,
501+
<DefaultSelectClause as SelectClauseExpression<F>>::SelectClauseSqlType: NotNull,
502+
{
503+
type Output =
504+
SelectStatement<F, SelectClause<Nullable<F::DefaultSelection>>, D, W, O, L, Of, G>;
505+
506+
fn nullable(self) -> Self::Output {
507+
SelectStatement::new(
508+
SelectClause(Nullable::new(self.from.default_selection())),
509+
self.from,
510+
self.distinct,
511+
self.where_clause,
512+
self.order,
513+
self.limit,
514+
self.offset,
515+
self.group_by,
516+
self.locking,
517+
)
518+
}
519+
}

diesel/src/query_builder/select_statement/mod.rs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,12 @@ use super::select_clause::*;
2626
use super::where_clause::*;
2727
use super::{AstPass, Query, QueryFragment};
2828
use backend::Backend;
29-
use expression::nullable::Nullable;
3029
use expression::subselect::ValidSubselect;
3130
use expression::*;
3231
use query_builder::SelectQuery;
3332
use query_source::joins::{AppendSelection, Inner, Join};
3433
use query_source::*;
3534
use result::QueryResult;
36-
use sql_types::NotNull;
3735

3836
#[derive(Debug, Clone, Copy, QueryId)]
3937
#[doc(hidden)]
@@ -103,26 +101,6 @@ impl<F> SelectStatement<F> {
103101
}
104102
}
105103

106-
impl<F, S, D, W, O, L, Of, G, LC> SelectStatement<F, S, D, W, O, L, Of, G, LC>
107-
where
108-
S: SelectClauseExpression<F>,
109-
S::SelectClauseSqlType: NotNull,
110-
{
111-
pub fn nullable(self) -> SelectStatement<F, Nullable<S>, D, W, O, L, Of, G, LC> {
112-
SelectStatement {
113-
select: Nullable::new(self.select),
114-
from: self.from,
115-
distinct: self.distinct,
116-
where_clause: self.where_clause,
117-
order: self.order,
118-
limit: self.limit,
119-
offset: self.offset,
120-
group_by: self.group_by,
121-
locking: self.locking,
122-
}
123-
}
124-
}
125-
126104
impl<F, S, D, W, O, L, Of, G, LC> Query for SelectStatement<F, S, D, W, O, L, Of, G, LC>
127105
where
128106
S: SelectClauseExpression<F>,

diesel/src/query_dsl/mod.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub mod limit_dsl;
3535
#[doc(hidden)]
3636
pub mod load_dsl;
3737
mod locking_dsl;
38+
mod nullable_select_dsl;
3839
mod offset_dsl;
3940
mod order_dsl;
4041
mod save_changes_dsl;
@@ -67,6 +68,7 @@ pub mod methods {
6768
#[allow(deprecated)]
6869
pub use super::locking_dsl::ForUpdateDsl;
6970
pub use super::locking_dsl::{LockingDsl, ModifyLockDsl};
71+
pub use super::nullable_select_dsl::NullableSelectDsl;
7072
pub use super::offset_dsl::OffsetDsl;
7173
pub use super::order_dsl::{OrderDsl, ThenOrderDsl};
7274
pub use super::select_dsl::SelectDsl;
@@ -1045,6 +1047,45 @@ pub trait QueryDsl: Sized {
10451047
{
10461048
methods::SingleValueDsl::single_value(self)
10471049
}
1050+
1051+
/// Coerce the SQL type of the select clause to it's nullable equivalent.
1052+
///
1053+
/// This is use full for writing queries that contain subselects on non null
1054+
/// fields comparing them to nullable fields.
1055+
/// ```rust
1056+
/// # #[macro_use] extern crate diesel;
1057+
/// # include!("../doctest_setup.rs");
1058+
/// #
1059+
/// # fn main() {
1060+
/// # run_test();
1061+
/// # }
1062+
/// #
1063+
/// # fn run_test() -> QueryResult<()> {
1064+
/// table! {
1065+
/// users {
1066+
/// id -> Integer,
1067+
/// name -> Text,
1068+
/// }
1069+
/// }
1070+
///
1071+
/// table! {
1072+
/// posts {
1073+
/// id -> Integer,
1074+
/// by_user -> Nullable<Text>,
1075+
/// }
1076+
/// }
1077+
///
1078+
/// # let _: Vec<(i32, Option<String>)> =
1079+
/// posts::table.filter(
1080+
/// posts::by_user.eq_any(users::table.select(users::name).nullable())
1081+
/// ).load(&connection)?;
1082+
/// # }
1083+
fn nullable(self) -> NullableSelect<Self>
1084+
where
1085+
Self: methods::NullableSelectDsl,
1086+
{
1087+
methods::NullableSelectDsl::nullable(self)
1088+
}
10481089
}
10491090

10501091
impl<T: Table> QueryDsl for T {}

0 commit comments

Comments
 (0)