Skip to content

Commit c26d299

Browse files
committed
Add a bare select function for select statements with no from clause
In addition to this function, I have also added a `sql` function, which is shorthand for `SqlLiteral` (but only takes `&str`, not `String`), and `in_time_zone` which is not part of the public API. `sql` takes `&str`, instead of `T: Into<String>`, because the first type parameter is always going to need to be passed, and I find `sql::<Timestamp, _>` ugly. There's always `SqlLiteral::new` if you need to pass a `String`, or you can just deref it to `str` and accept the copying. I had hoped to eliminate `query_sql_params`, but there's one test we have that's using it which requires a where clause. I can't just hack it and do `select(sql("1 WHERE ..."))` because it uses bind params. I'll implement `FilterDsl` for `BareSelectStatement` at some point. I'd also like to remove `query_sql`, as I think that `select(sql` covers it suffciently. I've opted to leave `in_time_zone` out of the public API, as it should actually have the return type of `Timestamptz`, and I'm really unsure how various infix predicates with the lhs being `Timestamp` and the rhs being `Timestamptz` are going to interact. We might be able to get away with just not allowing it and have it be a non-issue, similarly to `VarChar` and `Text`, but it needs further exploration. Fixes diesel-rs#97.
1 parent b551d91 commit c26d299

13 files changed

Lines changed: 183 additions & 78 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,19 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
1010
* Added support for mapping `types::Timestamp` to/from `chrono::NaiveDateTime`.
1111
Add `features = ["chrono"]` to enable.
1212

13+
* Added a top level `select` function for select statements with no from clause.
14+
This is primarily intended to be used for testing Diesel itself, but it has
15+
been added to the public API as it will likely be useful for third party
16+
crates in the future. `select(foo).from(bar)` might be a supported API in the
17+
future as an alternative to `bar.select(foo)`.
18+
19+
* Added `expression::dsl::sql` as a helper function for constructing
20+
`SqlLiteral` nodes. This is primarily intended to be used for testing Diesel
21+
itself, but is part of the public API as an escape hatch if our query builder
22+
DSL proves inadequate for a specific case. Use of this function in any
23+
production code is discouraged as it is inherently unsafe and avoids real type
24+
checking.
25+
1326
### Changed
1427

1528
* Rename both the `#[derive(Queriable)]` attribute and the `Queriable` trait to
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use expression::{Expression, SelectableExpression};
2+
use query_builder::{QueryBuilder, BuildQueryResult};
3+
use types::{Timestamp, VarChar};
4+
5+
pub struct AtTimeZone<Ts, Tz> {
6+
timestamp: Ts,
7+
timezone: Tz,
8+
}
9+
10+
impl<Ts, Tz> AtTimeZone<Ts, Tz> {
11+
pub fn new(timestamp: Ts, timezone: Tz) -> Self {
12+
AtTimeZone {
13+
timestamp: timestamp,
14+
timezone: timezone,
15+
}
16+
}
17+
}
18+
19+
impl<Ts, Tz> Expression for AtTimeZone<Ts, Tz> where
20+
Ts: Expression<SqlType=Timestamp>,
21+
Tz: Expression<SqlType=VarChar>,
22+
{
23+
// FIXME: This should be Timestamptz when we support that type
24+
type SqlType = Timestamp;
25+
26+
fn to_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult {
27+
try!(self.timestamp.to_sql(out));
28+
out.push_sql(" AT TIME ZONE ");
29+
self.timezone.to_sql(out)
30+
}
31+
}
32+
33+
impl<Ts, Tz, Qs> SelectableExpression<Qs> for AtTimeZone<Ts, Tz> where
34+
AtTimeZone<Ts, Tz>: Expression,
35+
{}

diesel/src/expression/expression_methods/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
pub mod global_expression_methods;
88
pub mod bool_expression_methods;
99
pub mod text_expression_methods;
10+
#[doc(hidden)]
11+
pub mod timestamp_expression_methods;
1012

1113
pub use self::global_expression_methods::ExpressionMethods;
1214
pub use self::bool_expression_methods::BoolExpressionMethods;
1315
pub use self::text_expression_methods::{TextExpressionMethods, VarCharExpressionMethods};
16+
#[doc(hidden)]
17+
pub use self::timestamp_expression_methods::TimestampExpressionMethods;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use expression::{AsExpression, Expression};
2+
use expression::date_and_time::AtTimeZone;
3+
use types::{VarChar, Timestamp};
4+
5+
pub trait TimestampExpressionMethods: Expression<SqlType=Timestamp> + Sized {
6+
/// Returns a PostgreSQL "AT TIME ZONE" expression
7+
fn at_time_zone<T>(self, timezone: T) -> AtTimeZone<Self, T::Expression> where
8+
T: AsExpression<VarChar>,
9+
{
10+
AtTimeZone::new(self, timezone.as_expression())
11+
}
12+
}
13+
14+
impl<T: Expression<SqlType=Timestamp>> TimestampExpressionMethods for T {}

diesel/src/expression/mod.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ pub mod array_comparison;
2525
pub mod bound;
2626
#[doc(hidden)]
2727
pub mod count;
28+
#[doc(hidden)]
29+
pub mod date_and_time;
2830
pub mod expression_methods;
2931
pub mod extensions;
3032
#[doc(hidden)]
@@ -42,14 +44,11 @@ pub mod sql_literal;
4244
/// generic to export by default. This module exists to conveniently glob import
4345
/// in functions where you need them.
4446
pub mod dsl {
45-
#[doc(inline)]
46-
pub use super::array_comparison::any;
47-
#[doc(inline)]
48-
pub use super::count::{count, count_star};
49-
#[doc(inline)]
50-
pub use super::functions::date_and_time::*;
51-
#[doc(inline)]
52-
pub use super::functions::aggregate_ordering::*;
47+
#[doc(inline)] pub use super::array_comparison::any;
48+
#[doc(inline)] pub use super::count::{count, count_star};
49+
#[doc(inline)] pub use super::functions::date_and_time::*;
50+
#[doc(inline)] pub use super::functions::aggregate_ordering::*;
51+
#[doc(inline)] pub use super::sql_literal::sql;
5352

5453
pub use super::extensions::*;
5554
}

diesel/src/expression/sql_literal.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,7 @@ impl<ST: NativeSqlType> Expression for SqlLiteral<ST> {
3232

3333
impl<QS, ST: NativeSqlType> SelectableExpression<QS> for SqlLiteral<ST> {
3434
}
35+
36+
pub fn sql<ST: NativeSqlType>(sql: &str) -> SqlLiteral<ST> {
37+
SqlLiteral::new(sql.into())
38+
}

diesel/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ pub mod prelude {
7474
pub use connection::{Connection, Cursor};
7575
pub use prelude::*;
7676
#[doc(inline)]
77-
pub use query_builder::functions::{insert, update, delete};
77+
pub use query_builder::functions::{insert, update, delete, select};
7878
pub use result::Error::NotFound;
7979
#[doc(inline)]
8080
pub use types::structs::data_types;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use expression::SelectableExpression;
2+
use super::{Query, QueryFragment, QueryBuilder, BuildQueryResult};
3+
4+
pub struct BareSelectStatement<T> {
5+
expression: T,
6+
}
7+
8+
impl<T> BareSelectStatement<T> {
9+
pub fn new(expression: T) -> Self {
10+
BareSelectStatement {
11+
expression: expression,
12+
}
13+
}
14+
}
15+
16+
impl<T> QueryFragment for BareSelectStatement<T> where
17+
T: SelectableExpression<()>,
18+
{
19+
fn to_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult {
20+
out.push_sql("SELECT ");
21+
self.expression.to_sql(out)
22+
}
23+
}
24+
25+
impl<T> Query for BareSelectStatement<T> where
26+
T: SelectableExpression<()>,
27+
{
28+
type SqlType = T::SqlType;
29+
}

diesel/src/query_builder/functions.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::{UpdateTarget, IncompleteUpdateStatement};
22
use super::delete_statement::DeleteStatement;
3+
use super::BareSelectStatement;
34
use super::IncompleteInsertStatement;
45

56
/// Creates an update statement. Helpers for updating a single row can be
@@ -107,3 +108,14 @@ pub fn delete<T: UpdateTarget>(source: T) -> DeleteStatement<T> {
107108
pub fn insert<T>(records: T) -> IncompleteInsertStatement<T> {
108109
IncompleteInsertStatement::new(records)
109110
}
111+
112+
/// Creates a bare select statement, with no from clause. Primarily used for
113+
/// testing diesel itself, but likely useful for third party crates as well. The
114+
/// given expressions must be selectable from anywhere.
115+
///
116+
/// Note: You must use `get_result` and not `first` to get a single value out of
117+
/// this, or you will get a vague compiler error (the reason being you cannot
118+
/// call `limit` on the type returned by this function).
119+
pub fn select<T>(expression: T) -> BareSelectStatement<T> {
120+
BareSelectStatement::new(expression)
121+
}

diesel/src/query_builder/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
pub mod pg;
44
pub mod debug;
55

6+
mod bare_select_statement;
67
mod delete_statement;
78
#[doc(hidden)]
89
pub mod functions;
@@ -14,6 +15,8 @@ mod where_clause;
1415
pub mod insert_statement;
1516
pub mod update_statement;
1617

18+
#[doc(hidden)]
19+
pub use self::bare_select_statement::BareSelectStatement;
1720
#[doc(hidden)]
1821
pub use self::select_statement::SelectStatement;
1922
#[doc(inline)]

0 commit comments

Comments
 (0)