From 9d4054c62db3b3d92c24fc6e58d40b3011f74ba4 Mon Sep 17 00:00:00 2001 From: Rasmus Kaj Date: Sun, 7 Aug 2016 00:43:17 +0200 Subject: [PATCH] Add support for ordering with NULLS FIRST or NULLS LAST in postgres. --- CHANGELOG.md | 4 + .../src/pg/expression/expression_methods.rs | 92 +++++++++++++++++++ diesel/src/pg/expression/predicates.rs | 3 + 3 files changed, 99 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 390ebeff51..cf9ef5e51e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/ ## Unreleased +* Added support for PostgreSQL `NULLS FIRST` and `NULLS LAST` when sorting. + See http://docs.diesel.rs/diesel/prelude/trait.SortExpressionMethods.html + for details. + ## [0.7.0] - 2016-08-01 ### Added diff --git a/diesel/src/pg/expression/expression_methods.rs b/diesel/src/pg/expression/expression_methods.rs index 77baeb3584..eca9018f0e 100644 --- a/diesel/src/pg/expression/expression_methods.rs +++ b/diesel/src/pg/expression/expression_methods.rs @@ -211,3 +211,95 @@ impl ArrayExpressionMethods for T where T: Expression>, { } + +use expression::predicates::{Asc, Desc}; + +pub trait SortExpressionMethods : Sized { + /// Specify that nulls should come before other values in this ordering. + /// Normally, nulls come last when sorting in ascending order and first + /// when sorting in descending order. + /// + /// # Example + /// + /// ```rust + /// # #[macro_use] extern crate diesel; + /// # include!("src/doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # table! { + /// # foos { + /// # id -> Integer, + /// # foo -> Nullable, + /// # } + /// # } + /// # + /// # fn main() { + /// # let connection = connection_no_data(); + /// # connection.execute("DROP TABLE IF EXISTS foos").unwrap(); + /// connection.execute("CREATE TABLE foos (id SERIAL PRIMARY KEY, foo INTEGER)").unwrap(); + /// connection.execute("INSERT INTO foos (foo) VALUES (NULL), (1), (2)").unwrap(); + /// + /// # use self::foos::dsl::*; + /// assert_eq!(Ok(vec![Some(1), Some(2), None]), + /// foos.select(foo).order(foo.asc()).load(&connection)); + /// assert_eq!(Ok(vec![None, Some(1), Some(2)]), + /// foos.select(foo).order(foo.asc().nulls_first()).load(&connection)); + /// # connection.execute("DROP TABLE foos").unwrap(); + /// # } + /// ``` + fn nulls_first(self) -> NullsFirst { + NullsFirst::new(self) + } + + /// Specify that nulls should come after other values in this ordering. + /// Normally, nulls come last when sorting in ascending order and first + /// when sorting in descending order. + /// + /// # Example + /// + /// ```rust + /// # #[macro_use] extern crate diesel; + /// # include!("src/doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # table! { + /// # foos { + /// # id -> Integer, + /// # foo -> Nullable, + /// # } + /// # } + /// # + /// # fn main() { + /// # let connection = connection_no_data(); + /// # connection.execute("DROP TABLE IF EXISTS foos").unwrap(); + /// connection.execute("CREATE TABLE foos (id SERIAL PRIMARY KEY, foo INTEGER)").unwrap(); + /// connection.execute("INSERT INTO foos (foo) VALUES (NULL), (1), (2)").unwrap(); + /// + /// # use self::foos::dsl::*; + /// assert_eq!(Ok(vec![None, Some(2), Some(1)]), + /// foos.select(foo).order(foo.desc()).load(&connection)); + /// assert_eq!(Ok(vec![Some(2), Some(1), None]), + /// foos.select(foo).order(foo.desc().nulls_last()).load(&connection)); + /// # connection.execute("DROP TABLE foos").unwrap(); + /// # } + /// ``` + fn nulls_last(self) -> NullsLast { + NullsLast::new(self) + } +} + +impl SortExpressionMethods for Asc {} + +impl SortExpressionMethods for Desc {} diff --git a/diesel/src/pg/expression/predicates.rs b/diesel/src/pg/expression/predicates.rs index f058e612c4..b9274fff93 100644 --- a/diesel/src/pg/expression/predicates.rs +++ b/diesel/src/pg/expression/predicates.rs @@ -1,6 +1,9 @@ use pg::Pg; +use query_builder::QueryBuilder; infix_predicate!(IsNotDistinctFrom, " IS NOT DISTINCT FROM ", backend: Pg); infix_predicate!(OverlapsWith, " && ", backend: Pg); infix_predicate!(Contains, " @> ", backend: Pg); infix_predicate!(IsContainedBy, " <@ ", backend: Pg); +postfix_expression!(NullsFirst, " NULLS FIRST", ()); +postfix_expression!(NullsLast, " NULLS LAST", ());