Skip to content

Commit dddb2ce

Browse files
committed
fix(parser): gate EXCLUDE by PG dialect; parse OPERATOR() and element ordering
Three related fixes to the `EXCLUDE` table-constraint arm: - Guard the match on `PostgreSqlDialect | GenericDialect` so MySQL, SQLite, and others can continue to use `exclude` as a column name. Previously the arm fired on any dialect and hard-errored once the expected continuation was missing, instead of falling through to `parse_column_def`. - Extend `parse_exclusion_element` to parse the optional `opclass`, `ASC`/`DESC`, and `NULLS FIRST`/`LAST` qualifiers that precede `WITH <op>`, matching the PG `index_elem` grammar. - Add `parse_exclusion_operator` so the schema-qualified `OPERATOR(schema.op)` form is consumed as one unit. The previous single-token lookahead silently stopped at `OPERATOR` and left the parenthesised path to corrupt the surrounding parse.
1 parent a189d8f commit dddb2ce

1 file changed

Lines changed: 49 additions & 11 deletions

File tree

src/parser/mod.rs

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9915,16 +9915,18 @@ impl<'a> Parser<'a> {
99159915
.into(),
99169916
))
99179917
}
9918-
Token::Word(w) if w.keyword == Keyword::EXCLUDE => {
9918+
Token::Word(w)
9919+
if w.keyword == Keyword::EXCLUDE
9920+
&& dialect_of!(self is PostgreSqlDialect | GenericDialect) =>
9921+
{
99199922
let index_method = if self.parse_keyword(Keyword::USING) {
99209923
Some(self.parse_identifier()?)
99219924
} else {
99229925
None
99239926
};
99249927

99259928
self.expect_token(&Token::LParen)?;
9926-
let elements =
9927-
self.parse_comma_separated(|p| p.parse_exclusion_element())?;
9929+
let elements = self.parse_comma_separated(|p| p.parse_exclusion_element())?;
99289930
self.expect_token(&Token::RParen)?;
99299931

99309932
let include = if self.parse_keyword(Keyword::INCLUDE) {
@@ -9972,19 +9974,55 @@ impl<'a> Parser<'a> {
99729974

99739975
fn parse_exclusion_element(&mut self) -> Result<ExclusionElement, ParserError> {
99749976
let expr = self.parse_expr()?;
9977+
9978+
// `index_elem` grammar: [ opclass ] [ ASC | DESC ] [ NULLS FIRST | LAST ]
9979+
let operator_class: Option<ObjectName> = if self
9980+
.peek_one_of_keywords(&[Keyword::ASC, Keyword::DESC, Keyword::NULLS, Keyword::WITH])
9981+
.is_some()
9982+
{
9983+
None
9984+
} else {
9985+
self.maybe_parse(|p| p.parse_object_name(false))?
9986+
};
9987+
let order = self.parse_order_by_options()?;
9988+
99759989
self.expect_keyword_is(Keyword::WITH)?;
9990+
let operator = self.parse_exclusion_operator()?;
9991+
9992+
Ok(ExclusionElement {
9993+
expr,
9994+
operator_class,
9995+
order,
9996+
operator,
9997+
})
9998+
}
9999+
10000+
/// Parse the operator that follows `WITH` in an `EXCLUDE` element.
10001+
///
10002+
/// Accepts either a single operator token (e.g. `=`, `&&`, `<->`) or the
10003+
/// Postgres `OPERATOR(schema.op)` form for schema-qualified operators.
10004+
fn parse_exclusion_operator(&mut self) -> Result<String, ParserError> {
10005+
if self.parse_keyword(Keyword::OPERATOR) {
10006+
self.expect_token(&Token::LParen)?;
10007+
let mut parts = vec![];
10008+
loop {
10009+
self.advance_token();
10010+
parts.push(self.get_current_token().to_string());
10011+
if !self.consume_token(&Token::Period) {
10012+
break;
10013+
}
10014+
}
10015+
self.expect_token(&Token::RParen)?;
10016+
return Ok(format!("OPERATOR({})", parts.join(".")));
10017+
}
10018+
997610019
let operator_token = self.next_token();
997710020
match &operator_token.token {
9978-
Token::EOF
9979-
| Token::RParen
9980-
| Token::Comma
9981-
| Token::SemiColon => {
9982-
return self.expected("exclusion operator", operator_token);
10021+
Token::EOF | Token::RParen | Token::Comma | Token::SemiColon => {
10022+
self.expected("exclusion operator", operator_token)
998310023
}
9984-
_ => {}
10024+
_ => Ok(operator_token.token.to_string()),
998510025
}
9986-
let operator = operator_token.token.to_string();
9987-
Ok(ExclusionElement { expr, operator })
998810026
}
998910027

999010028
fn parse_optional_nulls_distinct(&mut self) -> Result<NullsDistinctOption, ParserError> {

0 commit comments

Comments
 (0)