mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-08 01:15:00 +00:00
Add support for TOP before ALL/DISTINCT (#1495)
This commit is contained in:
parent
05821cc7db
commit
a5b0092506
10 changed files with 67 additions and 7 deletions
|
@ -279,6 +279,8 @@ pub struct Select {
|
|||
pub distinct: Option<Distinct>,
|
||||
/// MSSQL syntax: `TOP (<N>) [ PERCENT ] [ WITH TIES ]`
|
||||
pub top: Option<Top>,
|
||||
/// Whether the top was located before `ALL`/`DISTINCT`
|
||||
pub top_before_distinct: bool,
|
||||
/// projection expressions
|
||||
pub projection: Vec<SelectItem>,
|
||||
/// INTO
|
||||
|
@ -327,12 +329,20 @@ impl fmt::Display for Select {
|
|||
write!(f, " {value_table_mode}")?;
|
||||
}
|
||||
|
||||
if let Some(ref top) = self.top {
|
||||
if self.top_before_distinct {
|
||||
write!(f, " {top}")?;
|
||||
}
|
||||
}
|
||||
if let Some(ref distinct) = self.distinct {
|
||||
write!(f, " {distinct}")?;
|
||||
}
|
||||
if let Some(ref top) = self.top {
|
||||
write!(f, " {top}")?;
|
||||
if !self.top_before_distinct {
|
||||
write!(f, " {top}")?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, " {}", display_comma_separated(&self.projection))?;
|
||||
|
||||
if let Some(ref into) = self.into {
|
||||
|
|
|
@ -600,6 +600,12 @@ pub trait Dialect: Debug + Any {
|
|||
fn supports_notify(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns true if this dialect expects the the `TOP` option
|
||||
/// before the `ALL`/`DISTINCT` options in a `SELECT` statement.
|
||||
fn supports_top_before_distinct(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents the operators for which precedence must be defined
|
||||
|
|
|
@ -68,4 +68,10 @@ impl Dialect for RedshiftSqlDialect {
|
|||
fn supports_connect_by(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Redshift expects the `TOP` option before the `ALL/DISTINCT` option:
|
||||
/// <https://docs.aws.amazon.com/redshift/latest/dg/r_SELECT_list.html#r_SELECT_list-parameters>
|
||||
fn supports_top_before_distinct(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9193,13 +9193,16 @@ impl<'a> Parser<'a> {
|
|||
None
|
||||
};
|
||||
|
||||
let mut top_before_distinct = false;
|
||||
let mut top = None;
|
||||
if self.dialect.supports_top_before_distinct() && self.parse_keyword(Keyword::TOP) {
|
||||
top = Some(self.parse_top()?);
|
||||
top_before_distinct = true;
|
||||
}
|
||||
let distinct = self.parse_all_or_distinct()?;
|
||||
|
||||
let top = if self.parse_keyword(Keyword::TOP) {
|
||||
Some(self.parse_top()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if !self.dialect.supports_top_before_distinct() && self.parse_keyword(Keyword::TOP) {
|
||||
top = Some(self.parse_top()?);
|
||||
}
|
||||
|
||||
let projection = self.parse_projection()?;
|
||||
|
||||
|
@ -9342,6 +9345,7 @@ impl<'a> Parser<'a> {
|
|||
Ok(Select {
|
||||
distinct,
|
||||
top,
|
||||
top_before_distinct,
|
||||
projection,
|
||||
into,
|
||||
from,
|
||||
|
|
|
@ -40,6 +40,7 @@ fn parse_map_access_expr() {
|
|||
Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![UnnamedExpr(MapAccess {
|
||||
column: Box::new(Identifier(Ident {
|
||||
value: "string_values".to_string(),
|
||||
|
|
|
@ -379,6 +379,7 @@ fn parse_update_set_from() {
|
|||
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![
|
||||
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("name"))),
|
||||
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("id"))),
|
||||
|
@ -4649,6 +4650,7 @@ fn test_parse_named_window() {
|
|||
let expected = Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![
|
||||
SelectItem::ExprWithAlias {
|
||||
expr: Expr::Function(Function {
|
||||
|
@ -5289,6 +5291,7 @@ fn parse_interval_and_or_xor() {
|
|||
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![UnnamedExpr(Expr::Identifier(Ident {
|
||||
value: "col".to_string(),
|
||||
quote_style: None,
|
||||
|
@ -7367,6 +7370,7 @@ fn lateral_function() {
|
|||
let expected = Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions {
|
||||
opt_ilike: None,
|
||||
opt_exclude: None,
|
||||
|
@ -8215,6 +8219,7 @@ fn parse_merge() {
|
|||
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::Wildcard(
|
||||
WildcardAdditionalOptions::default()
|
||||
)],
|
||||
|
@ -9803,6 +9808,7 @@ fn parse_unload() {
|
|||
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![UnnamedExpr(Expr::Identifier(Ident::new("cola"))),],
|
||||
into: None,
|
||||
from: vec![TableWithJoins {
|
||||
|
@ -9978,6 +9984,7 @@ fn parse_connect_by() {
|
|||
let expect_query = Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![
|
||||
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("employee_id"))),
|
||||
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("manager_id"))),
|
||||
|
@ -10064,6 +10071,7 @@ fn parse_connect_by() {
|
|||
Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![
|
||||
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("employee_id"))),
|
||||
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("manager_id"))),
|
||||
|
@ -11475,3 +11483,13 @@ fn parse_notify_channel() {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_top() {
|
||||
let dialects = all_dialects_where(|d| d.supports_top_before_distinct());
|
||||
dialects.one_statement_parses_to("SELECT ALL * FROM tbl", "SELECT * FROM tbl");
|
||||
dialects.verified_stmt("SELECT TOP 3 * FROM tbl");
|
||||
dialects.one_statement_parses_to("SELECT TOP 3 ALL * FROM tbl", "SELECT TOP 3 * FROM tbl");
|
||||
dialects.verified_stmt("SELECT TOP 3 DISTINCT * FROM tbl");
|
||||
dialects.verified_stmt("SELECT TOP 3 DISTINCT a, b, c FROM tbl");
|
||||
}
|
||||
|
|
|
@ -261,6 +261,7 @@ fn test_select_union_by_name() {
|
|||
left: Box::<SetExpr>::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions {
|
||||
opt_ilike: None,
|
||||
opt_exclude: None,
|
||||
|
@ -301,6 +302,7 @@ fn test_select_union_by_name() {
|
|||
right: Box::<SetExpr>::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions {
|
||||
opt_ilike: None,
|
||||
opt_exclude: None,
|
||||
|
|
|
@ -114,6 +114,7 @@ fn parse_create_procedure() {
|
|||
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::UnnamedExpr(Expr::Value(number("1")))],
|
||||
into: None,
|
||||
from: vec![],
|
||||
|
@ -514,6 +515,7 @@ fn parse_substring_in_select() {
|
|||
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: Some(Distinct::Distinct),
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::UnnamedExpr(Expr::Substring {
|
||||
expr: Box::new(Expr::Identifier(Ident {
|
||||
value: "description".to_string(),
|
||||
|
|
|
@ -957,6 +957,7 @@ fn parse_escaped_quote_identifiers_with_escape() {
|
|||
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident {
|
||||
value: "quoted ` identifier".into(),
|
||||
quote_style: Some('`'),
|
||||
|
@ -1007,6 +1008,7 @@ fn parse_escaped_quote_identifiers_with_no_escape() {
|
|||
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident {
|
||||
value: "quoted `` identifier".into(),
|
||||
quote_style: Some('`'),
|
||||
|
@ -1050,6 +1052,7 @@ fn parse_escaped_backticks_with_escape() {
|
|||
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident {
|
||||
value: "`quoted identifier`".into(),
|
||||
quote_style: Some('`'),
|
||||
|
@ -1097,6 +1100,7 @@ fn parse_escaped_backticks_with_no_escape() {
|
|||
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident {
|
||||
value: "``quoted identifier``".into(),
|
||||
quote_style: Some('`'),
|
||||
|
@ -1741,6 +1745,7 @@ fn parse_select_with_numeric_prefix_column_name() {
|
|||
Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident::new(
|
||||
"123col_$@123abc"
|
||||
)))],
|
||||
|
@ -1795,6 +1800,7 @@ fn parse_select_with_concatenation_of_exp_number_and_numeric_prefix_column() {
|
|||
Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![
|
||||
SelectItem::UnnamedExpr(Expr::Value(number("123e4"))),
|
||||
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("123col_$@123abc")))
|
||||
|
@ -2295,6 +2301,7 @@ fn parse_substring_in_select() {
|
|||
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: Some(Distinct::Distinct),
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::UnnamedExpr(Expr::Substring {
|
||||
expr: Box::new(Expr::Identifier(Ident {
|
||||
value: "description".to_string(),
|
||||
|
@ -2616,6 +2623,7 @@ fn parse_hex_string_introducer() {
|
|||
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::UnnamedExpr(Expr::IntroducedString {
|
||||
introducer: "_latin1".to_string(),
|
||||
value: Value::HexStringLiteral("4D7953514C".to_string())
|
||||
|
|
|
@ -1165,6 +1165,7 @@ fn parse_copy_to() {
|
|||
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![
|
||||
SelectItem::ExprWithAlias {
|
||||
expr: Expr::Value(number("42")),
|
||||
|
@ -2505,6 +2506,7 @@ fn parse_array_subquery_expr() {
|
|||
left: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::UnnamedExpr(Expr::Value(number("1")))],
|
||||
into: None,
|
||||
from: vec![],
|
||||
|
@ -2525,6 +2527,7 @@ fn parse_array_subquery_expr() {
|
|||
right: Box::new(SetExpr::Select(Box::new(Select {
|
||||
distinct: None,
|
||||
top: None,
|
||||
top_before_distinct: false,
|
||||
projection: vec![SelectItem::UnnamedExpr(Expr::Value(number("2")))],
|
||||
into: None,
|
||||
from: vec![],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue