Skip to content

Commit d55aab1

Browse files
committed
Implement 'min' function based on SQLs 'MIN'
1 parent aab253e commit d55aab1

5 files changed

Lines changed: 115 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
1515
added to `Cargo.toml`, you'll gain `Arbitrary` implementations for everything
1616
in `diesel::data_types`.
1717

18+
* Add `min` function that mirrors SQLs MIN.
19+
1820
## [0.2.0] - 2015-11-30
1921

2022
### Added

diesel/src/expression/min.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use query_builder::{QueryBuilder, BuildQueryResult};
2+
use super::{Expression, SelectableExpression};
3+
use types::{SqlOrd, NativeSqlType};
4+
5+
/// Represents a SQL `MIN` function. This function can only take types which are
6+
/// ordered.
7+
pub fn min<ST, T>(t: T) -> Min<T> where
8+
ST: NativeSqlType + SqlOrd,
9+
T: Expression<SqlType=ST>,
10+
{
11+
Min {
12+
target: t,
13+
}
14+
}
15+
16+
#[derive(Debug, Clone, Copy)]
17+
pub struct Min<T: Expression> {
18+
target: T,
19+
}
20+
21+
impl<T: Expression> Expression for Min<T> {
22+
type SqlType = T::SqlType;
23+
24+
fn to_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult {
25+
out.push_sql("MIN(");
26+
try!(self.target.to_sql(out));
27+
out.push_sql(")");
28+
Ok(())
29+
}
30+
}
31+
32+
impl<T: Expression, QS> SelectableExpression<QS> for Min<T> {
33+
}

diesel/src/expression/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ pub mod helper_types;
3535
#[doc(hidden)]
3636
pub mod max;
3737
#[doc(hidden)]
38+
pub mod min;
39+
#[doc(hidden)]
3840
pub mod ordering;
3941
#[doc(hidden)]
4042
pub mod predicates;
@@ -52,6 +54,8 @@ pub mod dsl {
5254
pub use super::functions::date_and_time::*;
5355
#[doc(inline)]
5456
pub use super::max::max;
57+
#[doc(inline)]
58+
pub use super::min::min;
5559

5660
pub use super::extensions::*;
5761
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#[macro_use]
2+
extern crate diesel;
3+
4+
use diesel::*;
5+
use diesel::expression::min;
6+
7+
table! {
8+
stuff (b) {
9+
b -> Bool,
10+
}
11+
}
12+
13+
fn main() {
14+
let source = stuff::table.select(min(stuff::b));
15+
//~^ ERROR E0277
16+
}

diesel_tests/tests/expressions/mod.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fn test_count_max() {
7373
}
7474

7575
#[test]
76-
fn max_returns_same_type_as_expression_being_maxed() {
76+
fn max_returns_same_type_as_expression_being_maximized() {
7777
let connection = connection();
7878
setup_users_table(&connection);
7979
let source = users.select(max(name));
@@ -136,6 +136,65 @@ fn max_accepts_all_numeric_string_and_date_types() {
136136
let _ = users.select(max(arbitrary::<types::Nullable<types::Text>>()));
137137
}
138138

139+
#[test]
140+
fn test_count_min() {
141+
use self::numbers::columns::*;
142+
use self::numbers::table as numbers;
143+
144+
let connection = connection();
145+
connection.execute("CREATE TABLE numbers (n integer)").unwrap();
146+
connection.execute("INSERT INTO numbers (n) VALUES (2), (1), (5)").unwrap();
147+
let source = numbers.select(min(n));
148+
149+
assert_eq!(Ok(1), source.first(&connection));
150+
connection.execute("DELETE FROM numbers WHERE n = 1").unwrap();
151+
assert_eq!(Ok(2), source.first(&connection));
152+
}
153+
154+
#[test]
155+
fn min_returns_same_type_as_expression_being_minimized() {
156+
let connection = connection();
157+
setup_users_table(&connection);
158+
let source = users.select(min(name));
159+
160+
let data: &[_] = &[
161+
NewUser::new("B", None),
162+
NewUser::new("C", None),
163+
NewUser::new("A", None),
164+
];
165+
insert(data).into(users).execute(&connection).unwrap();
166+
assert_eq!(Ok("A".to_string()), source.first(&connection));
167+
connection.execute("DELETE FROM users WHERE name = 'A'").unwrap();
168+
assert_eq!(Ok("B".to_string()), source.first(&connection));
169+
}
170+
171+
#[test]
172+
fn min_accepts_all_numeric_string_and_date_types() {
173+
let _ = users.select(min(arbitrary::<types::SmallSerial>()));
174+
let _ = users.select(min(arbitrary::<types::Serial>()));
175+
let _ = users.select(min(arbitrary::<types::BigSerial>()));
176+
let _ = users.select(min(arbitrary::<types::SmallInt>()));
177+
let _ = users.select(min(arbitrary::<types::Integer>()));
178+
let _ = users.select(min(arbitrary::<types::BigInt>()));
179+
let _ = users.select(min(arbitrary::<types::Float>()));
180+
let _ = users.select(min(arbitrary::<types::Double>()));
181+
182+
let _ = users.select(min(arbitrary::<types::VarChar>()));
183+
let _ = users.select(min(arbitrary::<types::Text>()));
184+
185+
let _ = users.select(min(arbitrary::<types::Nullable<types::SmallSerial>>()));
186+
let _ = users.select(min(arbitrary::<types::Nullable<types::Serial>>()));
187+
let _ = users.select(min(arbitrary::<types::Nullable<types::BigSerial>>()));
188+
let _ = users.select(min(arbitrary::<types::Nullable<types::SmallInt>>()));
189+
let _ = users.select(min(arbitrary::<types::Nullable<types::Integer>>()));
190+
let _ = users.select(min(arbitrary::<types::Nullable<types::BigInt>>()));
191+
let _ = users.select(min(arbitrary::<types::Nullable<types::Float>>()));
192+
let _ = users.select(min(arbitrary::<types::Nullable<types::Double>>()));
193+
194+
let _ = users.select(min(arbitrary::<types::Nullable<types::VarChar>>()));
195+
let _ = users.select(min(arbitrary::<types::Nullable<types::Text>>()));
196+
}
197+
139198
sql_function!(coalesce, coalesce_t, (x: types::Nullable<types::VarChar>, y: types::VarChar) -> types::VarChar);
140199

141200
#[test]

0 commit comments

Comments
 (0)