add nulls first/last support to order by expression (#176)

Following `<sort specification list>` from the standard https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#_10_10_sort_specification_list
This commit is contained in:
QP Hou 2020-05-30 07:05:15 -07:00 committed by GitHub
parent c918ff042d
commit 418b9631ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 8 deletions

View file

@ -13,11 +13,13 @@ Check https://github.com/andygrove/sqlparser-rs/commits/master for undocumented
- Support Snowflake's `FROM (table_name)` (#155) - thanks @eyalleshem!
### Added
- Support basic forms of `CREATE INDEX` and `DROP INDEX` (#167) - thanks @mashuai!
- Support `ON { UPDATE | DELETE } { RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT }` in `FOREIGN KEY` constraints (#170) - thanks @c7hm4r!
- Support MSSQL `TOP (<N>) [ PERCENT ] [ WITH TIES ]` (#150) - thanks @alexkyllo!
- Support MySQL `LIMIT row_count OFFSET offset` (not followed by `ROW` or `ROWS`) and remember which variant was parsed (#158) - thanks @mjibson!
- Support PostgreSQL `CREATE TABLE IF NOT EXISTS table_name` (#163) - thanks @alex-dukhno!
- Support basic forms of `CREATE INDEX` and `DROP INDEX` (#167) - thanks @mashuai!
- Support `ON { UPDATE | DELETE } { RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT }` in `FOREIGN KEY` constraints (#170) - thanks @c7hm4r!
- Support basic forms of `CREATE SCHEMA` and `DROP SCHEMA` (#173) - thanks @alex-dukhno!
- Support `NULLS FIRST`/`LAST` in `ORDER BY` expressions (#176) - thanks @houqp!
### Fixed
- Report an error for unterminated string literals (#165)

View file

@ -374,20 +374,30 @@ pub enum JoinConstraint {
Natural,
}
/// SQL ORDER BY expression
/// An `ORDER BY` expression
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct OrderByExpr {
pub expr: Expr,
/// Optional `ASC` or `DESC`
pub asc: Option<bool>,
/// Optional `NULLS FIRST` or `NULLS LAST`
pub nulls_first: Option<bool>,
}
impl fmt::Display for OrderByExpr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.expr)?;
match self.asc {
Some(true) => write!(f, "{} ASC", self.expr),
Some(false) => write!(f, "{} DESC", self.expr),
None => write!(f, "{}", self.expr),
Some(true) => write!(f, " ASC")?,
Some(false) => write!(f, " DESC")?,
None => (),
}
match self.nulls_first {
Some(true) => write!(f, " NULLS FIRST")?,
Some(false) => write!(f, " NULLS LAST")?,
None => (),
}
Ok(())
}
}

View file

@ -220,6 +220,7 @@ define_keywords!(
LAG,
LANGUAGE,
LARGE,
LAST,
LAST_VALUE,
LATERAL,
LEAD,
@ -262,6 +263,7 @@ define_keywords!(
NTILE,
NULL,
NULLIF,
NULLS,
NUMERIC,
OBJECT,
OCTET_LENGTH,

View file

@ -2015,7 +2015,20 @@ impl Parser {
} else {
None
};
Ok(OrderByExpr { expr, asc })
let nulls_first = if self.parse_keywords(vec!["NULLS", "FIRST"]) {
Some(true)
} else if self.parse_keywords(vec!["NULLS", "LAST"]) {
Some(false)
} else {
None
};
Ok(OrderByExpr {
expr,
asc,
nulls_first,
})
}
/// Parse a TOP clause, MSSQL equivalent of LIMIT,

View file

@ -746,14 +746,17 @@ fn parse_select_order_by() {
OrderByExpr {
expr: Expr::Identifier(Ident::new("lname")),
asc: Some(true),
nulls_first: None,
},
OrderByExpr {
expr: Expr::Identifier(Ident::new("fname")),
asc: Some(false),
nulls_first: None,
},
OrderByExpr {
expr: Expr::Identifier(Ident::new("id")),
asc: None,
nulls_first: None,
},
],
select.order_by
@ -775,10 +778,35 @@ fn parse_select_order_by_limit() {
OrderByExpr {
expr: Expr::Identifier(Ident::new("lname")),
asc: Some(true),
nulls_first: None,
},
OrderByExpr {
expr: Expr::Identifier(Ident::new("fname")),
asc: Some(false),
nulls_first: None,
},
],
select.order_by
);
assert_eq!(Some(Expr::Value(number("2"))), select.limit);
}
#[test]
fn parse_select_order_by_nulls_order() {
let sql = "SELECT id, fname, lname FROM customer WHERE id < 5 \
ORDER BY lname ASC NULLS FIRST, fname DESC NULLS LAST LIMIT 2";
let select = verified_query(sql);
assert_eq!(
vec![
OrderByExpr {
expr: Expr::Identifier(Ident::new("lname")),
asc: Some(true),
nulls_first: Some(true),
},
OrderByExpr {
expr: Expr::Identifier(Ident::new("fname")),
asc: Some(false),
nulls_first: Some(false),
},
],
select.order_by
@ -1251,7 +1279,8 @@ fn parse_window_functions() {
partition_by: vec![],
order_by: vec![OrderByExpr {
expr: Expr::Identifier(Ident::new("dt")),
asc: Some(false)
asc: Some(false),
nulls_first: None,
}],
window_frame: None,
}),