From b235d6481506a6b81182d3b1156913ac18a0c912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Berkay=20=C5=9Eahin?= <124376117+berkaysynnada@users.noreply.github.com> Date: Fri, 8 Sep 2023 13:47:56 +0300 Subject: [PATCH] feat: Group By All (#964) Co-authored-by: Andrew Lamb --- src/ast/mod.rs | 2 +- src/ast/query.rs | 37 ++++++++++++++++++++++++++++++++--- src/parser/mod.rs | 8 ++++++-- tests/sqlparser_clickhouse.rs | 2 +- tests/sqlparser_common.rs | 26 ++++++++++++++++++------ tests/sqlparser_duckdb.rs | 8 ++++---- tests/sqlparser_mssql.rs | 4 ++-- tests/sqlparser_mysql.rs | 16 +++++++-------- tests/sqlparser_postgres.rs | 18 ++++++++--------- 9 files changed, 85 insertions(+), 36 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 7912e283e..68487e21c 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -36,7 +36,7 @@ pub use self::ddl::{ }; pub use self::operator::{BinaryOperator, UnaryOperator}; pub use self::query::{ - Cte, Distinct, ExceptSelectItem, ExcludeSelectItem, Fetch, IdentWithAlias, Join, + Cte, Distinct, ExceptSelectItem, ExcludeSelectItem, Fetch, GroupByExpr, IdentWithAlias, Join, JoinConstraint, JoinOperator, LateralView, LockClause, LockType, NamedWindowDefinition, NonBlock, Offset, OffsetRows, OrderByExpr, Query, RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Table, diff --git a/src/ast/query.rs b/src/ast/query.rs index fffafb06a..21756f368 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -218,7 +218,7 @@ pub struct Select { /// WHERE pub selection: Option, /// GROUP BY - pub group_by: Vec, + pub group_by: GroupByExpr, /// CLUSTER BY (Hive) pub cluster_by: Vec, /// DISTRIBUTE BY (Hive) @@ -259,8 +259,13 @@ impl fmt::Display for Select { if let Some(ref selection) = self.selection { write!(f, " WHERE {selection}")?; } - if !self.group_by.is_empty() { - write!(f, " GROUP BY {}", display_comma_separated(&self.group_by))?; + match &self.group_by { + GroupByExpr::All => write!(f, " GROUP BY ALL")?, + GroupByExpr::Expressions(exprs) => { + if !exprs.is_empty() { + write!(f, " GROUP BY {}", display_comma_separated(exprs))?; + } + } } if !self.cluster_by.is_empty() { write!( @@ -1222,3 +1227,29 @@ impl fmt::Display for SelectInto { write!(f, "INTO{}{}{} {}", temporary, unlogged, table, self.name) } } + +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub enum GroupByExpr { + /// ALL syntax of [Snowflake], and [DuckDB] + /// + /// [Snowflake]: + /// [DuckDB]: + All, + + /// Expressions + Expressions(Vec), +} + +impl fmt::Display for GroupByExpr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + GroupByExpr::All => write!(f, "GROUP BY ALL"), + GroupByExpr::Expressions(col_names) => { + let col_names = display_comma_separated(col_names); + write!(f, "GROUP BY ({col_names})") + } + } + } +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index cdff148a0..2d28b9dd1 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -5709,9 +5709,13 @@ impl<'a> Parser<'a> { }; let group_by = if self.parse_keywords(&[Keyword::GROUP, Keyword::BY]) { - self.parse_comma_separated(Parser::parse_group_by_expr)? + if self.parse_keyword(Keyword::ALL) { + GroupByExpr::All + } else { + GroupByExpr::Expressions(self.parse_comma_separated(Parser::parse_group_by_expr)?) + } } else { - vec![] + GroupByExpr::Expressions(vec![]) }; let cluster_by = if self.parse_keywords(&[Keyword::CLUSTER, Keyword::BY]) { diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 77b936d55..5241777e5 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -97,7 +97,7 @@ fn parse_map_access_expr() { right: Box::new(Expr::Value(Value::SingleQuotedString("foo".to_string()))) }) }), - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 1bf108cb0..be10914dc 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -247,7 +247,9 @@ fn parse_update_set_from() { }], lateral_views: vec![], selection: None, - group_by: vec![Expr::Identifier(Ident::new("id"))], + group_by: GroupByExpr::Expressions(vec![Expr::Identifier(Ident::new( + "id" + ))]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -1808,10 +1810,10 @@ fn parse_select_group_by() { let sql = "SELECT id, fname, lname FROM customer GROUP BY lname, fname"; let select = verified_only_select(sql); assert_eq!( - vec![ + GroupByExpr::Expressions(vec![ Expr::Identifier(Ident::new("lname")), Expr::Identifier(Ident::new("fname")), - ], + ]), select.group_by ); @@ -1822,6 +1824,18 @@ fn parse_select_group_by() { ); } +#[test] +fn parse_select_group_by_all() { + let sql = "SELECT id, fname, lname, SUM(order) FROM customer GROUP BY ALL"; + let select = verified_only_select(sql); + assert_eq!(GroupByExpr::All, select.group_by); + + one_statement_parses_to( + "SELECT id, fname, lname, SUM(order) FROM customer GROUP BY ALL", + "SELECT id, fname, lname, SUM(order) FROM customer GROUP BY ALL", + ); +} + #[test] fn parse_select_having() { let sql = "SELECT foo FROM bar GROUP BY foo HAVING COUNT(*) > 1"; @@ -3543,7 +3557,7 @@ fn test_parse_named_window() { }], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -3930,7 +3944,7 @@ fn parse_interval_and_or_xor() { }), }), }), - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -6333,7 +6347,7 @@ fn parse_merge() { }], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index 3587e8d90..0fe4bb0e7 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -161,7 +161,7 @@ fn test_select_union_by_name() { }], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -194,7 +194,7 @@ fn test_select_union_by_name() { }], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -236,7 +236,7 @@ fn test_select_union_by_name() { }], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -269,7 +269,7 @@ fn test_select_union_by_name() { }], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index c4e0f3274..9ed12ac21 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -103,7 +103,7 @@ fn parse_create_procedure() { from: vec![], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -519,7 +519,7 @@ fn parse_substring_in_select() { }], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 5c541ad37..1bb09b4b0 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -496,7 +496,7 @@ fn parse_escaped_quote_identifiers_with_escape() { from: vec![], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -538,7 +538,7 @@ fn parse_escaped_quote_identifiers_with_no_escape() { from: vec![], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -577,7 +577,7 @@ fn parse_escaped_backticks_with_escape() { from: vec![], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -616,7 +616,7 @@ fn parse_escaped_backticks_with_no_escape() { from: vec![], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -1100,7 +1100,7 @@ fn parse_select_with_numeric_prefix_column_name() { }], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -1149,7 +1149,7 @@ fn parse_select_with_concatenation_of_exp_number_and_numeric_prefix_column() { }], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -1325,7 +1325,7 @@ fn parse_substring_in_select() { }], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -1604,7 +1604,7 @@ fn parse_hex_string_introducer() { from: vec![], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 8a3989b30..9da58f791 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -991,7 +991,7 @@ fn parse_copy_to() { from: vec![], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), having: None, named_window: vec![], cluster_by: vec![], @@ -2020,7 +2020,7 @@ fn parse_array_subquery_expr() { from: vec![], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -2036,7 +2036,7 @@ fn parse_array_subquery_expr() { from: vec![], lateral_views: vec![], selection: None, - group_by: vec![], + group_by: GroupByExpr::Expressions(vec![]), cluster_by: vec![], distribute_by: vec![], sort_by: vec![], @@ -3322,14 +3322,14 @@ fn parse_select_group_by_grouping_sets() { "SELECT brand, size, sum(sales) FROM items_sold GROUP BY size, GROUPING SETS ((brand), (size), ())" ); assert_eq!( - vec![ + GroupByExpr::Expressions(vec![ Expr::Identifier(Ident::new("size")), Expr::GroupingSets(vec![ vec![Expr::Identifier(Ident::new("brand"))], vec![Expr::Identifier(Ident::new("size"))], vec![], ]), - ], + ]), select.group_by ); } @@ -3340,13 +3340,13 @@ fn parse_select_group_by_rollup() { "SELECT brand, size, sum(sales) FROM items_sold GROUP BY size, ROLLUP (brand, size)", ); assert_eq!( - vec![ + GroupByExpr::Expressions(vec![ Expr::Identifier(Ident::new("size")), Expr::Rollup(vec![ vec![Expr::Identifier(Ident::new("brand"))], vec![Expr::Identifier(Ident::new("size"))], ]), - ], + ]), select.group_by ); } @@ -3357,13 +3357,13 @@ fn parse_select_group_by_cube() { "SELECT brand, size, sum(sales) FROM items_sold GROUP BY size, CUBE (brand, size)", ); assert_eq!( - vec![ + GroupByExpr::Expressions(vec![ Expr::Identifier(Ident::new("size")), Expr::Cube(vec![ vec![Expr::Identifier(Ident::new("brand"))], vec![Expr::Identifier(Ident::new("size"))], ]), - ], + ]), select.group_by ); }