Skip to content

Commit e456051

Browse files
committed
FROM ONLY <table> clause
1 parent b2c5889 commit e456051

22 files changed

Lines changed: 544 additions & 223 deletions

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
6868

6969
* Added an option to `#[derive(Insertable)]` that let you insert `NULL` values instead of `DEFAULT` values for `Option<T>`
7070

71+
* Added support for the `FROM ONLY <table>` clause in Postgresql
72+
7173
### Removed
7274

7375
* All previously deprecated items have been removed.

diesel/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,7 @@ pub mod helper_types {
201201
//! `users.filter(first_name.eq("John")).order(last_name.asc()).limit(10)` would
202202
//! be `Limit<Order<FindBy<users, first_name, &str>, Asc<last_name>>>`
203203
use super::query_builder::combination_clause::{self, CombinationClause};
204-
use super::query_builder::locking_clause as lock;
205-
use super::query_builder::AsQuery;
204+
use super::query_builder::{locking_clause as lock, AsQuery};
206205
use super::query_dsl::methods::*;
207206
use super::query_dsl::*;
208207
use super::query_source::joins;
@@ -372,6 +371,11 @@ pub mod helper_types {
372371
/// Which conveniently lets you omit the exact join condition.
373372
pub type LeftJoinQuerySource<Left, Right, On = <Left as joins::JoinTo<Right>>::OnClause> =
374373
JoinQuerySource<Left, Right, joins::LeftOuter, On>;
374+
375+
/// Represents the return type of `.only()`
376+
#[cfg(feature = "postgres_backend")]
377+
pub type SelectFromOnly<T> =
378+
crate::query_builder::SelectStatement<crate::pg::query_builder::only_clause::Only<T>>;
375379
}
376380

377381
pub mod prelude {

diesel/src/macros/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,24 @@ macro_rules! __diesel_fix_sql_type_import {
2727
}
2828
}
2929

30+
#[macro_export]
31+
#[doc(hidden)]
32+
#[cfg(feature = "postgres_backend")]
33+
macro_rules! __diesel_internal_table_backend_specific_impls {
34+
($table:ident, $column_name:ident) => {
35+
impl $crate::expression::AppearsOnTable<$crate::query_builder::Only<$table>>
36+
for $column_name {}
37+
impl $crate::expression::SelectableExpression<$crate::query_builder::Only<$table>>
38+
for $column_name {}
39+
};
40+
}
41+
#[macro_export]
42+
#[doc(hidden)]
43+
#[cfg(not(feature = "postgres_backend"))]
44+
macro_rules! __diesel_internal_table_backend_specific_impls {
45+
($table:ident, $column_name:ident) => {};
46+
}
47+
3048
#[macro_export]
3149
#[doc(hidden)]
3250
macro_rules! __diesel_column {
@@ -135,6 +153,9 @@ macro_rules! __diesel_column {
135153
}
136154
}
137155

156+
157+
$crate::__diesel_internal_table_backend_specific_impls!($table, $column_name);
158+
138159
$crate::__diesel_generate_ops_impls_if_numeric!($column_name, $($Type)*);
139160
$crate::__diesel_generate_ops_impls_if_date_time!($column_name, $($Type)*);
140161
$crate::__diesel_generate_ops_impls_if_network!($column_name, $($Type)*);

diesel/src/pg/expression/extensions/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@
22
//! building expressions. These traits are not exported by default. The are also
33
//! re-exported in `diesel::dsl`
44
mod interval_dsl;
5+
mod only_dsl;
56

67
pub use self::interval_dsl::IntervalDsl;
8+
pub use self::only_dsl::OnlyDsl;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use crate::query_builder::Only;
2+
use crate::query_builder::{AsQuery, SelectStatement};
3+
use crate::query_source::Table;
4+
5+
/// The `only` method
6+
///
7+
/// This is only implemented for the Postgres backend.
8+
/// The `ONLY` clause is used to select only from one table and not any inherited ones.
9+
///
10+
/// Calling this function on a table (`mytable.only()`) will result in the SQL `FROM ONLY mytable`.
11+
///
12+
/// Example:
13+
///
14+
/// ```ignore
15+
/// let n_users_in_main_table = users::table
16+
/// .only()
17+
/// .select(count(users::id))
18+
/// .first::<i64>(connection)
19+
/// .unwrap();
20+
///
21+
/// ```
22+
/// Selects the number of entries in the `users` table excluding any rows found in inherited
23+
/// tables.
24+
pub trait OnlyDsl: AsQuery + Sized {
25+
/// See the trait-level docs.
26+
fn only(self) -> SelectStatement<Only<Self>> {
27+
SelectStatement::simple(Only { query: self })
28+
}
29+
}
30+
31+
impl<T: Table> OnlyDsl for T {}

diesel/src/pg/query_builder/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::result::QueryResult;
55
mod distinct_on;
66
mod limit_offset;
77
pub(crate) mod on_constraint;
8+
pub(crate) mod only_clause;
89
mod query_fragment_impls;
910
pub use self::distinct_on::DistinctOnClause;
1011

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//! `ONLY` clause.
2+
//!
3+
//! Can only be used on tables, e.g. `my_table::dsl::table.only().select(something)` should generate
4+
//! something like `SELECT something FROM ONLY my_table`.
5+
6+
use crate::pg::Pg;
7+
use crate::prelude::*;
8+
use crate::query_builder::{AstPass, QueryFragment, QueryId};
9+
10+
/// Represents a query with an `ONLY` clause.
11+
#[derive(Debug, QueryId)]
12+
pub struct Only<T> {
13+
pub(crate) query: T,
14+
}
15+
16+
impl<T> QueryFragment<Pg> for Only<T>
17+
where
18+
T: QueryFragment<Pg>,
19+
{
20+
fn walk_ast(&self, mut out: AstPass<Pg>) -> QueryResult<()> {
21+
out.push_sql("ONLY ");
22+
self.query.walk_ast(out.reborrow())?;
23+
Ok(())
24+
}
25+
}
26+
27+
impl<T> QuerySource for Only<T>
28+
where
29+
T: QuerySource,
30+
T::DefaultSelection: SelectableExpression<Only<T>>,
31+
{
32+
type FromClause = Only<T::FromClause>;
33+
type DefaultSelection = T::DefaultSelection;
34+
35+
fn from_clause(&self) -> Self::FromClause {
36+
Only {
37+
query: self.query.from_clause(),
38+
}
39+
}
40+
fn default_selection(&self) -> Self::DefaultSelection {
41+
self.query.default_selection()
42+
}
43+
}

diesel/src/query_builder/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ pub use self::returning_clause::ReturningClause;
7070

7171
pub(crate) use self::insert_statement::ColumnList;
7272

73+
#[cfg(feature = "postgres_backend")]
74+
pub use crate::pg::query_builder::only_clause::Only;
75+
7376
use std::error::Error;
7477

7578
use crate::backend::Backend;

diesel_compile_tests/tests/fail/aggregate_expression_requires_column_from_same_table.stderr

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
error[E0277]: the trait bound `posts::columns::id: SelectableExpression<users::table>` is not satisfied
2-
--> $DIR/aggregate_expression_requires_column_from_same_table.rs:19:38
2+
--> tests/fail/aggregate_expression_requires_column_from_same_table.rs:19:38
33
|
44
19 | let source = users::table.select(sum(posts::id));
55
| ^^^^^^^^^^^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::id`
66
|
77
= help: the following implementations were found:
88
<posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
9+
<posts::columns::id as SelectableExpression<Only<posts::table>>>
910
<posts::columns::id as SelectableExpression<SelectStatement<From>>>
1011
<posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
11-
<posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
12-
<posts::columns::id as SelectableExpression<posts::table>>
12+
and 2 others
1313
= note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `aggregate_folding::sum::sum<diesel::sql_types::Integer, posts::columns::id>`
1414
= note: required because of the requirements on the impl of `SelectDsl<aggregate_folding::sum::sum<diesel::sql_types::Integer, posts::columns::id>>` for `SelectStatement<users::table>`
1515

1616
error[E0277]: the trait bound `users::table: AppearsInFromClause<posts::table>` is not satisfied
17-
--> $DIR/aggregate_expression_requires_column_from_same_table.rs:19:31
17+
--> tests/fail/aggregate_expression_requires_column_from_same_table.rs:19:31
1818
|
1919
19 | let source = users::table.select(sum(posts::id));
2020
| ^^^^^^ the trait `AppearsInFromClause<posts::table>` is not implemented for `users::table`
@@ -28,22 +28,22 @@ error[E0277]: the trait bound `users::table: AppearsInFromClause<posts::table>`
2828
= note: required because of the requirements on the impl of `SelectDsl<aggregate_folding::sum::sum<diesel::sql_types::Integer, posts::columns::id>>` for `SelectStatement<users::table>`
2929

3030
error[E0277]: the trait bound `posts::columns::id: SelectableExpression<users::table>` is not satisfied
31-
--> $DIR/aggregate_expression_requires_column_from_same_table.rs:20:38
31+
--> tests/fail/aggregate_expression_requires_column_from_same_table.rs:20:38
3232
|
3333
20 | let source = users::table.select(avg(posts::id));
3434
| ^^^^^^^^^^^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::id`
3535
|
3636
= help: the following implementations were found:
3737
<posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
38+
<posts::columns::id as SelectableExpression<Only<posts::table>>>
3839
<posts::columns::id as SelectableExpression<SelectStatement<From>>>
3940
<posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
40-
<posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
41-
<posts::columns::id as SelectableExpression<posts::table>>
41+
and 2 others
4242
= note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `aggregate_folding::avg::avg<diesel::sql_types::Integer, posts::columns::id>`
4343
= note: required because of the requirements on the impl of `SelectDsl<aggregate_folding::avg::avg<diesel::sql_types::Integer, posts::columns::id>>` for `SelectStatement<users::table>`
4444

4545
error[E0277]: the trait bound `users::table: AppearsInFromClause<posts::table>` is not satisfied
46-
--> $DIR/aggregate_expression_requires_column_from_same_table.rs:20:31
46+
--> tests/fail/aggregate_expression_requires_column_from_same_table.rs:20:31
4747
|
4848
20 | let source = users::table.select(avg(posts::id));
4949
| ^^^^^^ the trait `AppearsInFromClause<posts::table>` is not implemented for `users::table`
@@ -57,22 +57,22 @@ error[E0277]: the trait bound `users::table: AppearsInFromClause<posts::table>`
5757
= note: required because of the requirements on the impl of `SelectDsl<aggregate_folding::avg::avg<diesel::sql_types::Integer, posts::columns::id>>` for `SelectStatement<users::table>`
5858

5959
error[E0277]: the trait bound `posts::columns::id: SelectableExpression<users::table>` is not satisfied
60-
--> $DIR/aggregate_expression_requires_column_from_same_table.rs:21:38
60+
--> tests/fail/aggregate_expression_requires_column_from_same_table.rs:21:38
6161
|
6262
21 | let source = users::table.select(max(posts::id));
6363
| ^^^^^^^^^^^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::id`
6464
|
6565
= help: the following implementations were found:
6666
<posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
67+
<posts::columns::id as SelectableExpression<Only<posts::table>>>
6768
<posts::columns::id as SelectableExpression<SelectStatement<From>>>
6869
<posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
69-
<posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
70-
<posts::columns::id as SelectableExpression<posts::table>>
70+
and 2 others
7171
= note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `aggregate_ordering::max::max<diesel::sql_types::Integer, posts::columns::id>`
7272
= note: required because of the requirements on the impl of `SelectDsl<aggregate_ordering::max::max<diesel::sql_types::Integer, posts::columns::id>>` for `SelectStatement<users::table>`
7373

7474
error[E0277]: the trait bound `users::table: AppearsInFromClause<posts::table>` is not satisfied
75-
--> $DIR/aggregate_expression_requires_column_from_same_table.rs:21:31
75+
--> tests/fail/aggregate_expression_requires_column_from_same_table.rs:21:31
7676
|
7777
21 | let source = users::table.select(max(posts::id));
7878
| ^^^^^^ the trait `AppearsInFromClause<posts::table>` is not implemented for `users::table`
@@ -86,22 +86,22 @@ error[E0277]: the trait bound `users::table: AppearsInFromClause<posts::table>`
8686
= note: required because of the requirements on the impl of `SelectDsl<aggregate_ordering::max::max<diesel::sql_types::Integer, posts::columns::id>>` for `SelectStatement<users::table>`
8787

8888
error[E0277]: the trait bound `posts::columns::id: SelectableExpression<users::table>` is not satisfied
89-
--> $DIR/aggregate_expression_requires_column_from_same_table.rs:22:38
89+
--> tests/fail/aggregate_expression_requires_column_from_same_table.rs:22:38
9090
|
9191
22 | let source = users::table.select(min(posts::id));
9292
| ^^^^^^^^^^^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::id`
9393
|
9494
= help: the following implementations were found:
9595
<posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
96+
<posts::columns::id as SelectableExpression<Only<posts::table>>>
9697
<posts::columns::id as SelectableExpression<SelectStatement<From>>>
9798
<posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
98-
<posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
99-
<posts::columns::id as SelectableExpression<posts::table>>
99+
and 2 others
100100
= note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `aggregate_ordering::min::min<diesel::sql_types::Integer, posts::columns::id>`
101101
= note: required because of the requirements on the impl of `SelectDsl<aggregate_ordering::min::min<diesel::sql_types::Integer, posts::columns::id>>` for `SelectStatement<users::table>`
102102

103103
error[E0277]: the trait bound `users::table: AppearsInFromClause<posts::table>` is not satisfied
104-
--> $DIR/aggregate_expression_requires_column_from_same_table.rs:22:31
104+
--> tests/fail/aggregate_expression_requires_column_from_same_table.rs:22:31
105105
|
106106
22 | let source = users::table.select(min(posts::id));
107107
| ^^^^^^ the trait `AppearsInFromClause<posts::table>` is not implemented for `users::table`

0 commit comments

Comments
 (0)