-
Notifications
You must be signed in to change notification settings - Fork 764
Honour operator precedence in IS [NOT] DISTINCT FROM
#2436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4071,11 +4071,14 @@ impl<'a> Parser<'a> { | |||||||||||
| } else if self.parse_keywords(&[Keyword::NOT, Keyword::UNKNOWN]) { | ||||||||||||
| Ok(Expr::IsNotUnknown(Box::new(expr))) | ||||||||||||
| } else if self.parse_keywords(&[Keyword::DISTINCT, Keyword::FROM]) { | ||||||||||||
| let expr2 = self.parse_expr()?; | ||||||||||||
| // The right operand binds no more loosely than `IS` | ||||||||||||
| // itself, so that e.g. `a IS DISTINCT FROM b AND c` | ||||||||||||
| // parses as `(a IS DISTINCT FROM b) AND c`. | ||||||||||||
| let expr2 = self.parse_subexpr(precedence)?; | ||||||||||||
|
zvonimir-dd marked this conversation as resolved.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-blocking, but a one-line note here would save the next reader from re-deriving why this is not
Suggested change
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Applied, on the |
||||||||||||
| Ok(Expr::IsDistinctFrom(Box::new(expr), Box::new(expr2))) | ||||||||||||
| } else if self.parse_keywords(&[Keyword::NOT, Keyword::DISTINCT, Keyword::FROM]) | ||||||||||||
| { | ||||||||||||
| let expr2 = self.parse_expr()?; | ||||||||||||
| let expr2 = self.parse_subexpr(precedence)?; | ||||||||||||
| Ok(Expr::IsNotDistinctFrom(Box::new(expr), Box::new(expr2))) | ||||||||||||
| } else if self.parse_keyword(Keyword::JSON) { | ||||||||||||
| self.parse_is_json_predicate(expr, false) | ||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4946,3 +4946,36 @@ fn parse_adjacent_string_literal_concatenation() { | |
| fn parse_group_by_with_rollup() { | ||
| mysql().verified_stmt("SELECT * FROM tbl GROUP BY col1, col2 WITH ROLLUP"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn parse_is_distinct_from_json_arrow_precedence() { | ||
| // MySQL's `->` binds tighter than `IS [NOT] DISTINCT FROM`, so the JSON | ||
| // extraction must stay inside the right operand. | ||
| assert_eq!( | ||
| Expr::IsDistinctFrom( | ||
| Box::new(Expr::Identifier(Ident::new("a"))), | ||
| Box::new(Expr::BinaryOp { | ||
| left: Box::new(Expr::Identifier(Ident::new("b"))), | ||
| op: BinaryOperator::Arrow, | ||
| right: Box::new(Expr::Value( | ||
| Value::SingleQuotedString("k".into()).with_empty_span() | ||
| )), | ||
| }), | ||
| ), | ||
| mysql_and_generic().verified_expr("a IS DISTINCT FROM b -> 'k'") | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| Expr::IsNotDistinctFrom( | ||
| Box::new(Expr::Identifier(Ident::new("a"))), | ||
| Box::new(Expr::BinaryOp { | ||
| left: Box::new(Expr::Identifier(Ident::new("b"))), | ||
| op: BinaryOperator::LongArrow, | ||
| right: Box::new(Expr::Value( | ||
| Value::SingleQuotedString("k".into()).with_empty_span() | ||
| )), | ||
| }), | ||
| ), | ||
| mysql_and_generic().verified_expr("a IS NOT DISTINCT FROM b ->> 'k'") | ||
| ); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since #[test]
fn parse_pg_other_operator_precedence() {
// The "any other operator" row -- `->`, `@>`, custom operators -- binds more
// tightly than comparison, `LIKE`, `BETWEEN` and the `IS` family, matching
// PostgreSQL's `%left Op OPERATOR RIGHT_ARROW` placement. Dialects that
// support lambda functions consume `->` in prefix position instead.
let dialects = all_dialects_where(|d| !d.supports_lambda_functions());
assert_eq!(
Expr::BinaryOp {
left: Box::new(Expr::BinaryOp {
left: Box::new(Expr::Identifier(Ident::new("a"))),
op: BinaryOperator::Arrow,
right: Box::new(Expr::value(Value::SingleQuotedString("k".to_string()))),
}),
op: BinaryOperator::Eq,
right: Box::new(Expr::Identifier(Ident::new("b"))),
},
dialects.verified_expr("a -> 'k' = b")
);
// A lambda is only recognised when `->` directly follows the parameter list,
// so a qualified left operand reaches this precedence in EVERY dialect --
// including those that support lambdas.
assert_eq!(
Expr::BinaryOp {
left: Box::new(Expr::BinaryOp {
left: Box::new(Expr::CompoundIdentifier(vec![
Ident::new("t"),
Ident::new("a"),
])),
op: BinaryOperator::Arrow,
right: Box::new(Expr::value(Value::SingleQuotedString("k".to_string()))),
}),
op: BinaryOperator::Eq,
right: Box::new(Expr::Identifier(Ident::new("b"))),
},
all_dialects().verified_expr("t.a -> 'k' = b")
);
}That second assertion is the one I would most want in the suite: it is the only arrow coverage that reaches DuckDB, ClickHouse, Databricks and Snowflake, which this hunk does affect whenever the left operand is not a bare identifier. (Note
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. One deviation: I kept a |
||
Uh oh!
There was an error while loading. Please reload this page.