Skip to content

Commit

Permalink
Support SELECT * EXCEPT/REPLACE syntax from ClickHouse (apache#1013)
Browse files Browse the repository at this point in the history
  • Loading branch information
lustefaniak authored and serprex committed Nov 6, 2023
1 parent e2fa7bb commit ddc3f27
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 13 deletions.
2 changes: 2 additions & 0 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,11 +438,13 @@ pub struct WildcardAdditionalOptions {
/// `[EXCLUDE...]`.
pub opt_exclude: Option<ExcludeSelectItem>,
/// `[EXCEPT...]`.
/// Clickhouse syntax: <https://clickhouse.com/docs/en/sql-reference/statements/select#except>
pub opt_except: Option<ExceptSelectItem>,
/// `[RENAME ...]`.
pub opt_rename: Option<RenameSelectItem>,
/// `[REPLACE]`
/// BigQuery syntax: <https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#select_replace>
/// Clickhouse syntax: <https://clickhouse.com/docs/en/sql-reference/statements/select#replace>
pub opt_replace: Option<ReplaceSelectItem>,
}

Expand Down
37 changes: 24 additions & 13 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7036,7 +7036,8 @@ impl<'a> Parser<'a> {
} else {
None
};
let opt_except = if dialect_of!(self is GenericDialect | BigQueryDialect) {
let opt_except = if dialect_of!(self is GenericDialect | BigQueryDialect | ClickHouseDialect)
{
self.parse_optional_select_item_except()?
} else {
None
Expand All @@ -7047,7 +7048,8 @@ impl<'a> Parser<'a> {
None
};

let opt_replace = if dialect_of!(self is GenericDialect | BigQueryDialect) {
let opt_replace = if dialect_of!(self is GenericDialect | BigQueryDialect | ClickHouseDialect)
{
self.parse_optional_select_item_replace()?
} else {
None
Expand Down Expand Up @@ -7090,18 +7092,27 @@ impl<'a> Parser<'a> {
&mut self,
) -> Result<Option<ExceptSelectItem>, ParserError> {
let opt_except = if self.parse_keyword(Keyword::EXCEPT) {
let idents = self.parse_parenthesized_column_list(Mandatory, false)?;
match &idents[..] {
[] => {
return self.expected(
"at least one column should be parsed by the expect clause",
self.peek_token(),
)?;
if self.peek_token().token == Token::LParen {
let idents = self.parse_parenthesized_column_list(Mandatory, false)?;
match &idents[..] {
[] => {
return self.expected(
"at least one column should be parsed by the expect clause",
self.peek_token(),
)?;
}
[first, idents @ ..] => Some(ExceptSelectItem {
first_element: first.clone(),
additional_elements: idents.to_vec(),
}),
}
[first, idents @ ..] => Some(ExceptSelectItem {
first_element: first.clone(),
additional_elements: idents.to_vec(),
}),
} else {
// Clickhouse allows EXCEPT column_name
let ident = self.parse_identifier()?;
Some(ExceptSelectItem {
first_element: ident,
additional_elements: vec![],
})
}
} else {
None
Expand Down
18 changes: 18 additions & 0 deletions tests/sqlparser_clickhouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,24 @@ fn parse_limit_by() {
);
}

#[test]
fn parse_select_star_except() {
clickhouse().verified_stmt("SELECT * EXCEPT (prev_status) FROM anomalies");
}

#[test]
fn parse_select_star_except_no_parens() {
clickhouse().one_statement_parses_to(
"SELECT * EXCEPT prev_status FROM anomalies",
"SELECT * EXCEPT (prev_status) FROM anomalies",
);
}

#[test]
fn parse_select_star_replace() {
clickhouse().verified_stmt("SELECT * REPLACE (i + 1 AS i) FROM columns_transformers");
}

fn clickhouse() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(ClickHouseDialect {})],
Expand Down

0 comments on commit ddc3f27

Please sign in to comment.