mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-24 07:54:06 +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!
|
- Support Snowflake's `FROM (table_name)` (#155) - thanks @eyalleshem!
|
||||||
|
|
||||||
### Added
|
### 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 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 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 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
|
### Fixed
|
||||||
- Report an error for unterminated string literals (#165)
|
- Report an error for unterminated string literals (#165)
|
||||||
|
|
|
@ -374,20 +374,30 @@ pub enum JoinConstraint {
|
||||||
Natural,
|
Natural,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// SQL ORDER BY expression
|
/// An `ORDER BY` expression
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct OrderByExpr {
|
pub struct OrderByExpr {
|
||||||
pub expr: Expr,
|
pub expr: Expr,
|
||||||
|
/// Optional `ASC` or `DESC`
|
||||||
pub asc: Option<bool>,
|
pub asc: Option<bool>,
|
||||||
|
/// Optional `NULLS FIRST` or `NULLS LAST`
|
||||||
|
pub nulls_first: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for OrderByExpr {
|
impl fmt::Display for OrderByExpr {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.expr)?;
|
||||||
match self.asc {
|
match self.asc {
|
||||||
Some(true) => write!(f, "{} ASC", self.expr),
|
Some(true) => write!(f, " ASC")?,
|
||||||
Some(false) => write!(f, "{} DESC", self.expr),
|
Some(false) => write!(f, " DESC")?,
|
||||||
None => write!(f, "{}", self.expr),
|
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,
|
LAG,
|
||||||
LANGUAGE,
|
LANGUAGE,
|
||||||
LARGE,
|
LARGE,
|
||||||
|
LAST,
|
||||||
LAST_VALUE,
|
LAST_VALUE,
|
||||||
LATERAL,
|
LATERAL,
|
||||||
LEAD,
|
LEAD,
|
||||||
|
@ -262,6 +263,7 @@ define_keywords!(
|
||||||
NTILE,
|
NTILE,
|
||||||
NULL,
|
NULL,
|
||||||
NULLIF,
|
NULLIF,
|
||||||
|
NULLS,
|
||||||
NUMERIC,
|
NUMERIC,
|
||||||
OBJECT,
|
OBJECT,
|
||||||
OCTET_LENGTH,
|
OCTET_LENGTH,
|
||||||
|
|
|
@ -2015,7 +2015,20 @@ impl Parser {
|
||||||
} else {
|
} else {
|
||||||
None
|
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,
|
/// Parse a TOP clause, MSSQL equivalent of LIMIT,
|
||||||
|
|
|
@ -746,14 +746,17 @@ fn parse_select_order_by() {
|
||||||
OrderByExpr {
|
OrderByExpr {
|
||||||
expr: Expr::Identifier(Ident::new("lname")),
|
expr: Expr::Identifier(Ident::new("lname")),
|
||||||
asc: Some(true),
|
asc: Some(true),
|
||||||
|
nulls_first: None,
|
||||||
},
|
},
|
||||||
OrderByExpr {
|
OrderByExpr {
|
||||||
expr: Expr::Identifier(Ident::new("fname")),
|
expr: Expr::Identifier(Ident::new("fname")),
|
||||||
asc: Some(false),
|
asc: Some(false),
|
||||||
|
nulls_first: None,
|
||||||
},
|
},
|
||||||
OrderByExpr {
|
OrderByExpr {
|
||||||
expr: Expr::Identifier(Ident::new("id")),
|
expr: Expr::Identifier(Ident::new("id")),
|
||||||
asc: None,
|
asc: None,
|
||||||
|
nulls_first: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
select.order_by
|
select.order_by
|
||||||
|
@ -775,10 +778,35 @@ fn parse_select_order_by_limit() {
|
||||||
OrderByExpr {
|
OrderByExpr {
|
||||||
expr: Expr::Identifier(Ident::new("lname")),
|
expr: Expr::Identifier(Ident::new("lname")),
|
||||||
asc: Some(true),
|
asc: Some(true),
|
||||||
|
nulls_first: None,
|
||||||
},
|
},
|
||||||
OrderByExpr {
|
OrderByExpr {
|
||||||
expr: Expr::Identifier(Ident::new("fname")),
|
expr: Expr::Identifier(Ident::new("fname")),
|
||||||
asc: Some(false),
|
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
|
select.order_by
|
||||||
|
@ -1251,7 +1279,8 @@ fn parse_window_functions() {
|
||||||
partition_by: vec![],
|
partition_by: vec![],
|
||||||
order_by: vec![OrderByExpr {
|
order_by: vec![OrderByExpr {
|
||||||
expr: Expr::Identifier(Ident::new("dt")),
|
expr: Expr::Identifier(Ident::new("dt")),
|
||||||
asc: Some(false)
|
asc: Some(false),
|
||||||
|
nulls_first: None,
|
||||||
}],
|
}],
|
||||||
window_frame: None,
|
window_frame: None,
|
||||||
}),
|
}),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue