mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-23 23:44:07 +00:00
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:
parent
c918ff042d
commit
418b9631ce
5 changed files with 64 additions and 8 deletions
|
@ -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)
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
}),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue