mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-10-07 04:30:24 +00:00
Support remaining pipe operators (#1879)
Some checks are pending
license / Release Audit Tool (RAT) (push) Waiting to run
Rust / codestyle (push) Waiting to run
Rust / lint (push) Waiting to run
Rust / benchmark-lint (push) Waiting to run
Rust / compile (push) Waiting to run
Rust / docs (push) Waiting to run
Rust / compile-no-std (push) Waiting to run
Rust / test (beta) (push) Waiting to run
Rust / test (nightly) (push) Waiting to run
Rust / test (stable) (push) Waiting to run
Some checks are pending
license / Release Audit Tool (RAT) (push) Waiting to run
Rust / codestyle (push) Waiting to run
Rust / lint (push) Waiting to run
Rust / benchmark-lint (push) Waiting to run
Rust / compile (push) Waiting to run
Rust / docs (push) Waiting to run
Rust / compile-no-std (push) Waiting to run
Rust / test (beta) (push) Waiting to run
Rust / test (nightly) (push) Waiting to run
Rust / test (stable) (push) Waiting to run
This commit is contained in:
parent
3bc94234df
commit
abd80f9ecb
3 changed files with 739 additions and 0 deletions
|
@ -9988,6 +9988,48 @@ impl<'a> Parser<'a> {
|
|||
Ok(IdentWithAlias { ident, alias })
|
||||
}
|
||||
|
||||
/// Parse `identifier [AS] identifier` where the AS keyword is optional
|
||||
fn parse_identifier_with_optional_alias(&mut self) -> Result<IdentWithAlias, ParserError> {
|
||||
let ident = self.parse_identifier()?;
|
||||
let _after_as = self.parse_keyword(Keyword::AS);
|
||||
let alias = self.parse_identifier()?;
|
||||
Ok(IdentWithAlias { ident, alias })
|
||||
}
|
||||
|
||||
/// Parse comma-separated list of parenthesized queries for pipe operators
|
||||
fn parse_pipe_operator_queries(&mut self) -> Result<Vec<Query>, ParserError> {
|
||||
self.parse_comma_separated(|parser| {
|
||||
parser.expect_token(&Token::LParen)?;
|
||||
let query = parser.parse_query()?;
|
||||
parser.expect_token(&Token::RParen)?;
|
||||
Ok(*query)
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse set quantifier for pipe operators that require DISTINCT. E.g. INTERSECT and EXCEPT
|
||||
fn parse_distinct_required_set_quantifier(
|
||||
&mut self,
|
||||
operator_name: &str,
|
||||
) -> Result<SetQuantifier, ParserError> {
|
||||
let quantifier = self.parse_set_quantifier(&Some(SetOperator::Intersect));
|
||||
match quantifier {
|
||||
SetQuantifier::Distinct | SetQuantifier::DistinctByName => Ok(quantifier),
|
||||
_ => Err(ParserError::ParserError(format!(
|
||||
"{operator_name} pipe operator requires DISTINCT modifier",
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse optional identifier alias (with or without AS keyword)
|
||||
fn parse_identifier_optional_alias(&mut self) -> Result<Option<Ident>, ParserError> {
|
||||
if self.parse_keyword(Keyword::AS) {
|
||||
Ok(Some(self.parse_identifier()?))
|
||||
} else {
|
||||
// Check if the next token is an identifier (implicit alias)
|
||||
self.maybe_parse(|parser| parser.parse_identifier())
|
||||
}
|
||||
}
|
||||
|
||||
/// Optionally parses an alias for a select list item
|
||||
fn maybe_parse_select_item_alias(&mut self) -> Result<Option<Ident>, ParserError> {
|
||||
fn validator(explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool {
|
||||
|
@ -11134,6 +11176,19 @@ impl<'a> Parser<'a> {
|
|||
Keyword::AGGREGATE,
|
||||
Keyword::ORDER,
|
||||
Keyword::TABLESAMPLE,
|
||||
Keyword::RENAME,
|
||||
Keyword::UNION,
|
||||
Keyword::INTERSECT,
|
||||
Keyword::EXCEPT,
|
||||
Keyword::CALL,
|
||||
Keyword::PIVOT,
|
||||
Keyword::UNPIVOT,
|
||||
Keyword::JOIN,
|
||||
Keyword::INNER,
|
||||
Keyword::LEFT,
|
||||
Keyword::RIGHT,
|
||||
Keyword::FULL,
|
||||
Keyword::CROSS,
|
||||
])?;
|
||||
match kw {
|
||||
Keyword::SELECT => {
|
||||
|
@ -11200,6 +11255,121 @@ impl<'a> Parser<'a> {
|
|||
let sample = self.parse_table_sample(TableSampleModifier::TableSample)?;
|
||||
pipe_operators.push(PipeOperator::TableSample { sample });
|
||||
}
|
||||
Keyword::RENAME => {
|
||||
let mappings =
|
||||
self.parse_comma_separated(Parser::parse_identifier_with_optional_alias)?;
|
||||
pipe_operators.push(PipeOperator::Rename { mappings });
|
||||
}
|
||||
Keyword::UNION => {
|
||||
let set_quantifier = self.parse_set_quantifier(&Some(SetOperator::Union));
|
||||
let queries = self.parse_pipe_operator_queries()?;
|
||||
pipe_operators.push(PipeOperator::Union {
|
||||
set_quantifier,
|
||||
queries,
|
||||
});
|
||||
}
|
||||
Keyword::INTERSECT => {
|
||||
let set_quantifier =
|
||||
self.parse_distinct_required_set_quantifier("INTERSECT")?;
|
||||
let queries = self.parse_pipe_operator_queries()?;
|
||||
pipe_operators.push(PipeOperator::Intersect {
|
||||
set_quantifier,
|
||||
queries,
|
||||
});
|
||||
}
|
||||
Keyword::EXCEPT => {
|
||||
let set_quantifier = self.parse_distinct_required_set_quantifier("EXCEPT")?;
|
||||
let queries = self.parse_pipe_operator_queries()?;
|
||||
pipe_operators.push(PipeOperator::Except {
|
||||
set_quantifier,
|
||||
queries,
|
||||
});
|
||||
}
|
||||
Keyword::CALL => {
|
||||
let function_name = self.parse_object_name(false)?;
|
||||
let function_expr = self.parse_function(function_name)?;
|
||||
if let Expr::Function(function) = function_expr {
|
||||
let alias = self.parse_identifier_optional_alias()?;
|
||||
pipe_operators.push(PipeOperator::Call { function, alias });
|
||||
} else {
|
||||
return Err(ParserError::ParserError(
|
||||
"Expected function call after CALL".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
Keyword::PIVOT => {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let aggregate_functions =
|
||||
self.parse_comma_separated(Self::parse_aliased_function_call)?;
|
||||
self.expect_keyword_is(Keyword::FOR)?;
|
||||
let value_column = self.parse_period_separated(|p| p.parse_identifier())?;
|
||||
self.expect_keyword_is(Keyword::IN)?;
|
||||
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let value_source = if self.parse_keyword(Keyword::ANY) {
|
||||
let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
|
||||
self.parse_comma_separated(Parser::parse_order_by_expr)?
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
PivotValueSource::Any(order_by)
|
||||
} else if self.peek_sub_query() {
|
||||
PivotValueSource::Subquery(self.parse_query()?)
|
||||
} else {
|
||||
PivotValueSource::List(
|
||||
self.parse_comma_separated(Self::parse_expr_with_alias)?,
|
||||
)
|
||||
};
|
||||
self.expect_token(&Token::RParen)?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
|
||||
let alias = self.parse_identifier_optional_alias()?;
|
||||
|
||||
pipe_operators.push(PipeOperator::Pivot {
|
||||
aggregate_functions,
|
||||
value_column,
|
||||
value_source,
|
||||
alias,
|
||||
});
|
||||
}
|
||||
Keyword::UNPIVOT => {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let value_column = self.parse_identifier()?;
|
||||
self.expect_keyword(Keyword::FOR)?;
|
||||
let name_column = self.parse_identifier()?;
|
||||
self.expect_keyword(Keyword::IN)?;
|
||||
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let unpivot_columns = self.parse_comma_separated(Parser::parse_identifier)?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
|
||||
self.expect_token(&Token::RParen)?;
|
||||
|
||||
let alias = self.parse_identifier_optional_alias()?;
|
||||
|
||||
pipe_operators.push(PipeOperator::Unpivot {
|
||||
value_column,
|
||||
name_column,
|
||||
unpivot_columns,
|
||||
alias,
|
||||
});
|
||||
}
|
||||
Keyword::JOIN
|
||||
| Keyword::INNER
|
||||
| Keyword::LEFT
|
||||
| Keyword::RIGHT
|
||||
| Keyword::FULL
|
||||
| Keyword::CROSS => {
|
||||
self.prev_token();
|
||||
let mut joins = self.parse_joins()?;
|
||||
if joins.len() != 1 {
|
||||
return Err(ParserError::ParserError(
|
||||
"Join pipe operator must have a single join".to_string(),
|
||||
));
|
||||
}
|
||||
let join = joins.swap_remove(0);
|
||||
pipe_operators.push(PipeOperator::Join(join))
|
||||
}
|
||||
unhandled => {
|
||||
return Err(ParserError::ParserError(format!(
|
||||
"`expect_one_of_keywords` further up allowed unhandled keyword: {unhandled:?}"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue