From 212ae9d5931f11071b9ab665b41f8ad3d8dd3236 Mon Sep 17 00:00:00 2001 From: Lukasz Stefaniak Date: Mon, 5 Jun 2023 15:06:57 +0200 Subject: [PATCH] clickhouse: add support for LIMIT BY --- src/ast/query.rs | 7 +++++++ src/parser/mod.rs | 13 ++++++++++++- tests/sqlparser_clickhouse.rs | 18 ++++++++++++++++++ tests/sqlparser_common.rs | 5 +++++ tests/sqlparser_mssql.rs | 3 +++ tests/sqlparser_mysql.rs | 9 +++++++++ tests/sqlparser_postgres.rs | 2 ++ 7 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index af35c37a3..d5f170791 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -35,6 +35,10 @@ pub struct Query { pub order_by: Vec, /// `LIMIT { | ALL }` pub limit: Option, + + /// `LIMIT { } BY { ,,... } }` + pub limit_by: Vec, + /// `OFFSET [ { ROW | ROWS } ]` pub offset: Option, /// `FETCH { FIRST | NEXT } [ PERCENT ] { ROW | ROWS } | { ONLY | WITH TIES }` @@ -58,6 +62,9 @@ impl fmt::Display for Query { if let Some(ref offset) = self.offset { write!(f, " {offset}")?; } + if !self.limit_by.is_empty() { + write!(f, " BY {}", display_separated(&self.limit_by, ", "))?; + } if let Some(ref fetch) = self.fetch { write!(f, " {fetch}")?; } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index a388a9137..dcd731a65 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -5431,6 +5431,7 @@ impl<'a> Parser<'a> { with, body: Box::new(SetExpr::Insert(insert)), limit: None, + limit_by: vec![], order_by: vec![], offset: None, fetch: None, @@ -5442,6 +5443,7 @@ impl<'a> Parser<'a> { with, body: Box::new(SetExpr::Update(update)), limit: None, + limit_by: vec![], order_by: vec![], offset: None, fetch: None, @@ -5468,7 +5470,7 @@ impl<'a> Parser<'a> { offset = Some(self.parse_offset()?) } - if dialect_of!(self is GenericDialect | MySqlDialect) + if dialect_of!(self is GenericDialect | MySqlDialect | ClickHouseDialect) && limit.is_some() && offset.is_none() && self.consume_token(&Token::Comma) @@ -5483,6 +5485,14 @@ impl<'a> Parser<'a> { } } + let limit_by = if dialect_of!(self is ClickHouseDialect | GenericDialect) + && self.parse_keyword(Keyword::BY) + { + self.parse_comma_separated(Parser::parse_expr)? + } else { + vec![] + }; + let fetch = if self.parse_keyword(Keyword::FETCH) { Some(self.parse_fetch()?) } else { @@ -5499,6 +5509,7 @@ impl<'a> Parser<'a> { body, order_by, limit, + limit_by, offset, fetch, locks, diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 936b0799a..9efe4a368 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -25,6 +25,7 @@ use sqlparser::ast::TableFactor::Table; use sqlparser::ast::*; use sqlparser::dialect::ClickHouseDialect; +use sqlparser::dialect::GenericDialect; #[test] fn parse_map_access_expr() { @@ -344,9 +345,26 @@ fn parse_double_equal() { ); } +#[test] +fn parse_limit_by() { + clickhouse_and_generic().verified_stmt( + r#"SELECT * FROM default.last_asset_runs_mv ORDER BY created_at DESC LIMIT 1 BY asset"#, + ); + clickhouse_and_generic().verified_stmt( + r#"SELECT * FROM default.last_asset_runs_mv ORDER BY created_at DESC LIMIT 1 BY asset, toStartOfDay(created_at)"#, + ); +} + fn clickhouse() -> TestedDialects { TestedDialects { dialects: vec![Box::new(ClickHouseDialect {})], options: None, } } + +fn clickhouse_and_generic() -> TestedDialects { + TestedDialects { + dialects: vec![Box::new(ClickHouseDialect {}), Box::new(GenericDialect {})], + options: None, + } +} diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index d73061f79..80e4cdf02 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -261,6 +261,7 @@ fn parse_update_set_from() { }))), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -2662,6 +2663,7 @@ fn parse_create_table_as_table() { }))), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -2685,6 +2687,7 @@ fn parse_create_table_as_table() { }))), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -3976,6 +3979,7 @@ fn parse_interval_and_or_xor() { }))), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -6392,6 +6396,7 @@ fn parse_merge() { }))), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 135e5d138..f9eb4d8fb 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -92,6 +92,7 @@ fn parse_create_procedure() { body: vec![Statement::Query(Box::new(Query { with: None, limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -493,6 +494,7 @@ fn parse_substring_in_select() { assert_eq!( Box::new(Query { with: None, + body: Box::new(SetExpr::Select(Box::new(Select { distinct: Some(Distinct::Distinct), top: None, @@ -532,6 +534,7 @@ fn parse_substring_in_select() { }))), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index f1a054bfb..80b9dcfd8 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -562,6 +562,7 @@ fn parse_escaped_quote_identifiers_with_escape() { }))), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -604,6 +605,7 @@ fn parse_escaped_quote_identifiers_with_no_escape() { }))), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -643,6 +645,7 @@ fn parse_escaped_backticks_with_escape() { }))), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -682,6 +685,7 @@ fn parse_escaped_backticks_with_no_escape() { }))), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -956,6 +960,7 @@ fn parse_simple_insert() { })), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -991,6 +996,7 @@ fn parse_empty_row_insert() { })), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -1049,6 +1055,7 @@ fn parse_insert_with_on_duplicate_update() { })), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -1428,6 +1435,7 @@ fn parse_substring_in_select() { }))), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -1708,6 +1716,7 @@ fn parse_hex_string_introducer() { }))), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index bb3857817..fe336bda7 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -1000,6 +1000,7 @@ fn parse_copy_to() { }))), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![], @@ -2046,6 +2047,7 @@ fn parse_array_subquery_expr() { }), order_by: vec![], limit: None, + limit_by: vec![], offset: None, fetch: None, locks: vec![],