diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 79edea7d1..eb8830bb1 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 b70017654..c9fcdecc9 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -214,7 +214,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) @@ -255,8 +255,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!( @@ -1218,3 +1223,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 348267fd8..6f81e1e4e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -5662,9 +5662,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 7346b9c0d..00901799f 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 093fac8c1..079e4695a 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -990,7 +990,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![], @@ -2019,7 +2019,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![], @@ -2035,7 +2035,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![], @@ -3321,14 +3321,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 ); } @@ -3339,13 +3339,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 ); } @@ -3356,13 +3356,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 ); }