From 0a777b2a65bc99e56ed944144566d1d6770e6a3d Mon Sep 17 00:00:00 2001 From: Andy Grove Date: Fri, 19 Aug 2022 06:00:54 -0600 Subject: [PATCH 1/8] Add support for filter during aggregation .. FILTER (WHERE ..) --- src/ast/query.rs | 6 ++++++ src/keywords.rs | 1 + src/parser.rs | 14 ++++++++++++++ tests/sqlparser_clickhouse.rs | 1 + tests/sqlparser_common.rs | 1 + tests/sqlparser_hive.rs | 6 ++++++ tests/sqlparser_mysql.rs | 3 +++ tests/sqlparser_postgres.rs | 3 +++ 8 files changed, 35 insertions(+) diff --git a/src/ast/query.rs b/src/ast/query.rs index bc5af9e5f..daf7b745b 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -142,6 +142,8 @@ pub struct Select { pub from: Vec, /// LATERAL VIEWs pub lateral_views: Vec, + /// FILTER (WHERE ..) + pub filter_during_agg: Option, /// WHERE pub selection: Option, /// GROUP BY @@ -170,6 +172,10 @@ impl fmt::Display for Select { write!(f, " {}", into)?; } + if let Some(ref filter) = self.filter_during_agg { + write!(f, " FILTER (WHERE {})", filter)?; + } + if !self.from.is_empty() { write!(f, " FROM {}", display_comma_separated(&self.from))?; } diff --git a/src/keywords.rs b/src/keywords.rs index a06ddc48d..64a34cc35 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -636,4 +636,5 @@ pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[ // Reserved only as a column alias in the `SELECT` clause Keyword::FROM, Keyword::INTO, + Keyword::FILTER, ]; diff --git a/src/parser.rs b/src/parser.rs index 4cfbdd23b..f34bb4762 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3623,6 +3623,19 @@ impl<'a> Parser<'a> { None }; + // filtering during aggregation + // FILTER (WHERE expr) + // https://trino.io/docs/current/functions/aggregate.html#filtering-during-aggregation + let filter_during_agg = if self.parse_keyword(Keyword::FILTER) { + self.expect_token(&Token::LParen)?; + self.expect_keyword(Keyword::WHERE)?; + let expr = self.parse_expr()?; + self.expect_token(&Token::RParen)?; + Some(expr) + } else { + None + }; + // Note that for keywords to be properly handled here, they need to be // added to `RESERVED_FOR_COLUMN_ALIAS` / `RESERVED_FOR_TABLE_ALIAS`, // otherwise they may be parsed as an alias as part of the `projection` @@ -3712,6 +3725,7 @@ impl<'a> Parser<'a> { top, projection, into, + filter_during_agg, from, lateral_views, selection, diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index a61df73cc..5f8e0ae62 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -55,6 +55,7 @@ fn parse_map_access_expr() { })], })], into: None, + filter_during_agg: None, from: vec![TableWithJoins { relation: Table { name: ObjectName(vec![Ident::new("foos")]), diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 65d699b3f..2118d84ad 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4957,6 +4957,7 @@ fn parse_merge() { top: None, projection: vec![SelectItem::Wildcard], into: None, + filter_during_agg: None, from: vec![TableWithJoins { relation: TableFactor::Table { name: ObjectName(vec![Ident::new("s"), Ident::new("foo")]), diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 4223ad5fa..0ffeef69f 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -276,6 +276,12 @@ fn parse_create_function() { ); } +#[test] +fn filtering_during_aggregation() { + let rename = "SELECT array_agg(name) FILTER (WHERE name IS NOT NULL) FROM region"; + println!("{}", hive().verified_stmt(rename)); +} + fn hive() -> TestedDialects { TestedDialects { dialects: vec![Box::new(HiveDialect {})], diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index f46d5d23e..9592bc7bb 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -453,6 +453,7 @@ fn parse_quote_identifiers_2() { quote_style: Some('`'), }))], into: None, + filter_during_agg: None, from: vec![], lateral_views: vec![], selection: None, @@ -487,6 +488,7 @@ fn parse_quote_identifiers_3() { quote_style: Some('`'), }))], into: None, + filter_during_agg: None, from: vec![], lateral_views: vec![], selection: None, @@ -922,6 +924,7 @@ fn parse_substring_in_select() { )))) })], into: None, + filter_during_agg: None, from: vec![TableWithJoins { relation: TableFactor::Table { name: ObjectName(vec![Ident { diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 3aaabc9e3..aff17f203 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -434,6 +434,7 @@ fn parse_update_set_from() { SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("id"))), ], into: None, + filter_during_agg: None, from: vec![TableWithJoins { relation: TableFactor::Table { name: ObjectName(vec![Ident::new("t1")]), @@ -1274,6 +1275,7 @@ fn parse_array_subquery_expr() { false, )))], into: None, + filter_during_agg: None, from: vec![], lateral_views: vec![], selection: None, @@ -1295,6 +1297,7 @@ fn parse_array_subquery_expr() { false, )))], into: None, + filter_during_agg: None, from: vec![], lateral_views: vec![], selection: None, From e2a8432bfd54fd7febfc32b6a17d354fe5174f05 Mon Sep 17 00:00:00 2001 From: Andy Grove Date: Fri, 19 Aug 2022 06:03:45 -0600 Subject: [PATCH 2/8] make this dialect specific --- src/dialect/hive.rs | 4 ++++ src/dialect/mod.rs | 4 ++++ src/parser.rs | 4 +++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/dialect/hive.rs b/src/dialect/hive.rs index 9b42857ec..ceb5488ef 100644 --- a/src/dialect/hive.rs +++ b/src/dialect/hive.rs @@ -36,4 +36,8 @@ impl Dialect for HiveDialect { || ch == '{' || ch == '}' } + + fn supports_filter_during_aggregation(&self) -> bool { + true + } } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index 46e8dda2c..1d3c9cf5f 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -67,6 +67,10 @@ pub trait Dialect: Debug + Any { fn is_identifier_start(&self, ch: char) -> bool; /// Determine if a character is a valid unquoted identifier character fn is_identifier_part(&self, ch: char) -> bool; + /// Does the dialect support `FILTER (WHERE expr)` for aggregate queries? + fn supports_filter_during_aggregation(&self) -> bool { + false + } /// Dialect-specific prefix parser override fn parse_prefix(&self, _parser: &mut Parser) -> Option> { // return None to fall back to the default behavior diff --git a/src/parser.rs b/src/parser.rs index f34bb4762..56719ab13 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3626,7 +3626,9 @@ impl<'a> Parser<'a> { // filtering during aggregation // FILTER (WHERE expr) // https://trino.io/docs/current/functions/aggregate.html#filtering-during-aggregation - let filter_during_agg = if self.parse_keyword(Keyword::FILTER) { + let filter_during_agg = if self.dialect.supports_filter_during_aggregation() + && self.parse_keyword(Keyword::FILTER) + { self.expect_token(&Token::LParen)?; self.expect_keyword(Keyword::WHERE)?; let expr = self.parse_expr()?; From 6101026f4d3c8b6e62c353167e563d357673522e Mon Sep 17 00:00:00 2001 From: Andy Grove Date: Fri, 19 Aug 2022 06:22:38 -0600 Subject: [PATCH 3/8] fix --- src/keywords.rs | 1 - src/parser.rs | 54 ++++++++++++++++++++++++++++++----------- tests/sqlparser_hive.rs | 6 +++++ 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/keywords.rs b/src/keywords.rs index 64a34cc35..a06ddc48d 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -636,5 +636,4 @@ pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[ // Reserved only as a column alias in the `SELECT` clause Keyword::FROM, Keyword::INTO, - Keyword::FILTER, ]; diff --git a/src/parser.rs b/src/parser.rs index 56719ab13..d2871fd91 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3626,14 +3626,21 @@ impl<'a> Parser<'a> { // filtering during aggregation // FILTER (WHERE expr) // https://trino.io/docs/current/functions/aggregate.html#filtering-during-aggregation - let filter_during_agg = if self.dialect.supports_filter_during_aggregation() - && self.parse_keyword(Keyword::FILTER) - { - self.expect_token(&Token::LParen)?; - self.expect_keyword(Keyword::WHERE)?; - let expr = self.parse_expr()?; - self.expect_token(&Token::RParen)?; - Some(expr) + let filter_during_agg = if self.dialect.supports_filter_during_aggregation() { + match self.peek_token() { + Token::Word(word) if word.value.as_str().to_lowercase() == "filter" => { + let i = self.index; + if self.consume_token(&Token::LParen) && self.parse_keyword(Keyword::WHERE) { + let expr = self.parse_expr()?; + self.expect_token(&Token::RParen)?; + Some(expr) + } else { + self.index = i; + None + } + } + _ => None, + } } else { None }; @@ -4526,12 +4533,31 @@ impl<'a> Parser<'a> { /// Parse a comma-delimited list of projections after SELECT pub fn parse_select_item(&mut self) -> Result { match self.parse_wildcard_expr()? { - WildcardExpr::Expr(expr) => self - .parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS) - .map(|alias| match alias { - Some(alias) => SelectItem::ExprWithAlias { expr, alias }, - None => SelectItem::UnnamedExpr(expr), - }), + WildcardExpr::Expr(expr) => { + let x = self.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS)?; + + if self.dialect.supports_filter_during_aggregation() { + match x { + Some(ident) if ident.value.to_lowercase() == "filter" => { + let i = self.index; + if self.consume_token(&Token::LParen) + && self.parse_keyword(Keyword::WHERE) + { + Ok(SelectItem::UnnamedExpr(expr)) + } else { + self.index = i; + Ok(SelectItem::ExprWithAlias { expr, alias: ident }) + } + } + _ => Ok(SelectItem::UnnamedExpr(expr)), + } + } else { + match x { + Some(alias) => Ok(SelectItem::ExprWithAlias { expr, alias }), + None => Ok(SelectItem::UnnamedExpr(expr)), + } + } + } WildcardExpr::QualifiedWildcard(prefix) => Ok(SelectItem::QualifiedWildcard(prefix)), WildcardExpr::Wildcard => Ok(SelectItem::Wildcard), } diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 0ffeef69f..eff58b8f3 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -282,6 +282,12 @@ fn filtering_during_aggregation() { println!("{}", hive().verified_stmt(rename)); } +#[test] +fn filter_as_alias() { + let rename = "SELECT name filter FROM region"; + println!("{}", hive().verified_stmt(rename)); +} + fn hive() -> TestedDialects { TestedDialects { dialects: vec![Box::new(HiveDialect {})], From 29276e21e95e1e23608d02dea6c76f4329535939 Mon Sep 17 00:00:00 2001 From: Andy Grove Date: Fri, 2 Sep 2022 10:58:12 -0600 Subject: [PATCH 4/8] save progress --- src/ast/query.rs | 11 +++++------ src/dialect/postgresql.rs | 4 ++++ src/parser.rs | 27 +++------------------------ tests/sqlparser_clickhouse.rs | 1 - tests/sqlparser_common.rs | 1 - tests/sqlparser_hive.rs | 19 ++++++++++++++++--- tests/sqlparser_mysql.rs | 3 --- tests/sqlparser_postgres.rs | 3 --- 8 files changed, 28 insertions(+), 41 deletions(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index daf7b745b..cc8ab8a21 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -142,8 +142,6 @@ pub struct Select { pub from: Vec, /// LATERAL VIEWs pub lateral_views: Vec, - /// FILTER (WHERE ..) - pub filter_during_agg: Option, /// WHERE pub selection: Option, /// GROUP BY @@ -172,10 +170,6 @@ impl fmt::Display for Select { write!(f, " {}", into)?; } - if let Some(ref filter) = self.filter_during_agg { - write!(f, " FILTER (WHERE {})", filter)?; - } - if !self.from.is_empty() { write!(f, " FROM {}", display_comma_separated(&self.from))?; } @@ -303,6 +297,8 @@ pub enum SelectItem { QualifiedWildcard(ObjectName), /// An unqualified `*` Wildcard, + /// Aggregate expression with filter such as `array_agg(name) FILTER (WHERE name IS NOT NULL)` + AggregateWithFilter { expr: Expr, filter: Expr }, } impl fmt::Display for SelectItem { @@ -310,6 +306,9 @@ impl fmt::Display for SelectItem { match &self { SelectItem::UnnamedExpr(expr) => write!(f, "{}", expr), SelectItem::ExprWithAlias { expr, alias } => write!(f, "{} AS {}", expr, alias), + SelectItem::AggregateWithFilter { expr, filter } => { + write!(f, "{} FILTER (WHERE {})", expr, filter) + } SelectItem::QualifiedWildcard(prefix) => write!(f, "{}.*", prefix), SelectItem::Wildcard => write!(f, "*"), } diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs index 04d64b9bf..b1f261b2e 100644 --- a/src/dialect/postgresql.rs +++ b/src/dialect/postgresql.rs @@ -42,6 +42,10 @@ impl Dialect for PostgreSqlDialect { None } } + + fn supports_filter_during_aggregation(&self) -> bool { + true + } } pub fn parse_comment(parser: &mut Parser) -> Result { diff --git a/src/parser.rs b/src/parser.rs index d2871fd91..985b8c8dd 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3623,28 +3623,6 @@ impl<'a> Parser<'a> { None }; - // filtering during aggregation - // FILTER (WHERE expr) - // https://trino.io/docs/current/functions/aggregate.html#filtering-during-aggregation - let filter_during_agg = if self.dialect.supports_filter_during_aggregation() { - match self.peek_token() { - Token::Word(word) if word.value.as_str().to_lowercase() == "filter" => { - let i = self.index; - if self.consume_token(&Token::LParen) && self.parse_keyword(Keyword::WHERE) { - let expr = self.parse_expr()?; - self.expect_token(&Token::RParen)?; - Some(expr) - } else { - self.index = i; - None - } - } - _ => None, - } - } else { - None - }; - // Note that for keywords to be properly handled here, they need to be // added to `RESERVED_FOR_COLUMN_ALIAS` / `RESERVED_FOR_TABLE_ALIAS`, // otherwise they may be parsed as an alias as part of the `projection` @@ -3734,7 +3712,6 @@ impl<'a> Parser<'a> { top, projection, into, - filter_during_agg, from, lateral_views, selection, @@ -4543,7 +4520,9 @@ impl<'a> Parser<'a> { if self.consume_token(&Token::LParen) && self.parse_keyword(Keyword::WHERE) { - Ok(SelectItem::UnnamedExpr(expr)) + let filter = self.parse_expr()?; + self.expect_token(&Token::RParen)?; + Ok(SelectItem::AggregateWithFilter { expr, filter }) } else { self.index = i; Ok(SelectItem::ExprWithAlias { expr, alias: ident }) diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 5f8e0ae62..a61df73cc 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -55,7 +55,6 @@ fn parse_map_access_expr() { })], })], into: None, - filter_during_agg: None, from: vec![TableWithJoins { relation: Table { name: ObjectName(vec![Ident::new("foos")]), diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 2118d84ad..65d699b3f 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4957,7 +4957,6 @@ fn parse_merge() { top: None, projection: vec![SelectItem::Wildcard], into: None, - filter_during_agg: None, from: vec![TableWithJoins { relation: TableFactor::Table { name: ObjectName(vec![Ident::new("s"), Ident::new("foo")]), diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index eff58b8f3..8839cea2b 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -278,16 +278,29 @@ fn parse_create_function() { #[test] fn filtering_during_aggregation() { - let rename = "SELECT array_agg(name) FILTER (WHERE name IS NOT NULL) FROM region"; + let rename = "SELECT \ + array_agg(name) FILTER (WHERE name IS NOT NULL), \ + array_agg(name) FILTER (WHERE name LIKE 'a%') \ + FROM region"; println!("{}", hive().verified_stmt(rename)); } #[test] -fn filter_as_alias() { - let rename = "SELECT name filter FROM region"; +fn filtering_during_aggregation_aliased() { + let rename = "SELECT \ + array_agg(name) FILTER (WHERE name IS NOT NULL) AS agg1, \ + array_agg(name) FILTER (WHERE name LIKE 'a%') AS agg2 \ + FROM region"; println!("{}", hive().verified_stmt(rename)); } +#[test] +fn filter_as_alias() { + let sql = "SELECT name filter FROM region"; + let expected = "SELECT name AS filter FROM region"; + println!("{}", hive().one_statement_parses_to(sql, expected)); +} + fn hive() -> TestedDialects { TestedDialects { dialects: vec![Box::new(HiveDialect {})], diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 9592bc7bb..f46d5d23e 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -453,7 +453,6 @@ fn parse_quote_identifiers_2() { quote_style: Some('`'), }))], into: None, - filter_during_agg: None, from: vec![], lateral_views: vec![], selection: None, @@ -488,7 +487,6 @@ fn parse_quote_identifiers_3() { quote_style: Some('`'), }))], into: None, - filter_during_agg: None, from: vec![], lateral_views: vec![], selection: None, @@ -924,7 +922,6 @@ fn parse_substring_in_select() { )))) })], into: None, - filter_during_agg: None, from: vec![TableWithJoins { relation: TableFactor::Table { name: ObjectName(vec![Ident { diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index aff17f203..3aaabc9e3 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -434,7 +434,6 @@ fn parse_update_set_from() { SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("id"))), ], into: None, - filter_during_agg: None, from: vec![TableWithJoins { relation: TableFactor::Table { name: ObjectName(vec![Ident::new("t1")]), @@ -1275,7 +1274,6 @@ fn parse_array_subquery_expr() { false, )))], into: None, - filter_during_agg: None, from: vec![], lateral_views: vec![], selection: None, @@ -1297,7 +1295,6 @@ fn parse_array_subquery_expr() { false, )))], into: None, - filter_during_agg: None, from: vec![], lateral_views: vec![], selection: None, From 93ac2f3fcbae7b17974558b68d3b7e670e5b519f Mon Sep 17 00:00:00 2001 From: Andy Grove Date: Fri, 2 Sep 2022 10:58:37 -0600 Subject: [PATCH 5/8] Merge remote-tracking branch 'upstream/main' into hive-filter From 9cd5782c225e9d5bfff5b6a73e5ccc09dc6259a2 Mon Sep 17 00:00:00 2001 From: Andy Grove Date: Fri, 2 Sep 2022 11:11:06 -0600 Subject: [PATCH 6/8] re-implement --- src/ast/mod.rs | 5 +++++ src/ast/query.rs | 5 ----- src/parser.rs | 42 ++++++++++++++++++++---------------------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 4f5fdb2eb..6c279518e 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -375,6 +375,8 @@ pub enum Expr { MapAccess { column: Box, keys: Vec }, /// Scalar function call e.g. `LEFT(foo, 5)` Function(Function), + /// Aggregate function with filter + AggregateExpressionWithFilter { expr: Box, filter: Box }, /// `CASE [] WHEN THEN ... [ELSE ] END` /// /// Note we only recognize a complete single expression as ``, @@ -571,6 +573,9 @@ impl fmt::Display for Expr { write!(f, " '{}'", &value::escape_single_quote_string(value)) } Expr::Function(fun) => write!(f, "{}", fun), + Expr::AggregateExpressionWithFilter { expr, filter } => { + write!(f, "{} FILTER (WHERE {})", expr, filter) + } Expr::Case { operand, conditions, diff --git a/src/ast/query.rs b/src/ast/query.rs index cc8ab8a21..bc5af9e5f 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -297,8 +297,6 @@ pub enum SelectItem { QualifiedWildcard(ObjectName), /// An unqualified `*` Wildcard, - /// Aggregate expression with filter such as `array_agg(name) FILTER (WHERE name IS NOT NULL)` - AggregateWithFilter { expr: Expr, filter: Expr }, } impl fmt::Display for SelectItem { @@ -306,9 +304,6 @@ impl fmt::Display for SelectItem { match &self { SelectItem::UnnamedExpr(expr) => write!(f, "{}", expr), SelectItem::ExprWithAlias { expr, alias } => write!(f, "{} AS {}", expr, alias), - SelectItem::AggregateWithFilter { expr, filter } => { - write!(f, "{} FILTER (WHERE {})", expr, filter) - } SelectItem::QualifiedWildcard(prefix) => write!(f, "{}.*", prefix), SelectItem::Wildcard => write!(f, "*"), } diff --git a/src/parser.rs b/src/parser.rs index 475c0ce59..4719b2321 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -4543,31 +4543,29 @@ impl<'a> Parser<'a> { pub fn parse_select_item(&mut self) -> Result { match self.parse_wildcard_expr()? { WildcardExpr::Expr(expr) => { - let x = self.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS)?; - - if self.dialect.supports_filter_during_aggregation() { - match x { - Some(ident) if ident.value.to_lowercase() == "filter" => { - let i = self.index; - if self.consume_token(&Token::LParen) - && self.parse_keyword(Keyword::WHERE) - { - let filter = self.parse_expr()?; - self.expect_token(&Token::RParen)?; - Ok(SelectItem::AggregateWithFilter { expr, filter }) - } else { - self.index = i; - Ok(SelectItem::ExprWithAlias { expr, alias: ident }) - } + let expr: Expr = if self.dialect.supports_filter_during_aggregation() + && self.parse_keyword(Keyword::FILTER) + { + let i = self.index; + if self.consume_token(&Token::LParen) && self.parse_keyword(Keyword::WHERE) { + let filter = self.parse_expr()?; + self.expect_token(&Token::RParen)?; + Expr::AggregateExpressionWithFilter { + expr: Box::new(expr), + filter: Box::new(filter), } - _ => Ok(SelectItem::UnnamedExpr(expr)), + } else { + self.index = i; + expr } } else { - match x { - Some(alias) => Ok(SelectItem::ExprWithAlias { expr, alias }), - None => Ok(SelectItem::UnnamedExpr(expr)), - } - } + expr + }; + self.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS) + .map(|alias| match alias { + Some(alias) => SelectItem::ExprWithAlias { expr, alias }, + None => SelectItem::UnnamedExpr(expr), + }) } WildcardExpr::QualifiedWildcard(prefix) => Ok(SelectItem::QualifiedWildcard(prefix)), WildcardExpr::Wildcard => Ok(SelectItem::Wildcard), From 96ff3492e63a2904f948774239d6cac9ebd9ee1e Mon Sep 17 00:00:00 2001 From: Andy Grove Date: Fri, 2 Sep 2022 11:14:31 -0600 Subject: [PATCH 7/8] fix --- src/parser.rs | 2 +- tests/sqlparser_hive.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 4719b2321..894bb84f1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -4546,7 +4546,7 @@ impl<'a> Parser<'a> { let expr: Expr = if self.dialect.supports_filter_during_aggregation() && self.parse_keyword(Keyword::FILTER) { - let i = self.index; + let i = self.index - 1; if self.consume_token(&Token::LParen) && self.parse_keyword(Keyword::WHERE) { let filter = self.parse_expr()?; self.expect_token(&Token::RParen)?; diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 8839cea2b..4e2fb5df2 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -294,6 +294,12 @@ fn filtering_during_aggregation_aliased() { println!("{}", hive().verified_stmt(rename)); } +/* +projection: [ExprWithAlias { expr: Identifier(Ident { value: "name", quote_style: None }), alias: Ident { value: "filter", quote_style: None } }], into: None, +projection: [UnnamedExpr(Identifier(Ident { value: "name", quote_style: None }))], into: None, + + */ + #[test] fn filter_as_alias() { let sql = "SELECT name filter FROM region"; From 315916b916da0f0669fe8bda2f12196124d0f2af Mon Sep 17 00:00:00 2001 From: Andy Grove Date: Fri, 2 Sep 2022 11:16:08 -0600 Subject: [PATCH 8/8] revert debug --- tests/sqlparser_hive.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 4e2fb5df2..8839cea2b 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -294,12 +294,6 @@ fn filtering_during_aggregation_aliased() { println!("{}", hive().verified_stmt(rename)); } -/* -projection: [ExprWithAlias { expr: Identifier(Ident { value: "name", quote_style: None }), alias: Ident { value: "filter", quote_style: None } }], into: None, -projection: [UnnamedExpr(Identifier(Ident { value: "name", quote_style: None }))], into: None, - - */ - #[test] fn filter_as_alias() { let sql = "SELECT name filter FROM region";