-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Improve formatting of binary expressions #3884
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 | ||||
---|---|---|---|---|---|---|
|
@@ -31,7 +31,7 @@ use datafusion_common::Result; | |||||
use datafusion_common::{plan_err, Column}; | ||||||
use datafusion_common::{DataFusionError, ScalarValue}; | ||||||
use std::fmt; | ||||||
use std::fmt::Write; | ||||||
use std::fmt::{Display, Formatter, Write}; | ||||||
use std::hash::{BuildHasher, Hash, Hasher}; | ||||||
use std::ops::Not; | ||||||
use std::sync::Arc; | ||||||
|
@@ -265,6 +265,58 @@ impl BinaryExpr { | |||||
pub fn new(left: Box<Expr>, op: Operator, right: Box<Expr>) -> Self { | ||||||
Self { left, op, right } | ||||||
} | ||||||
|
||||||
/// Get the operator precedence | ||||||
/// use https://www.postgresql.org/docs/7.0/operators.htm#AEN2026 as a reference | ||||||
pub fn precedence(&self) -> u8 { | ||||||
match self.op { | ||||||
Operator::Or => 5, | ||||||
Operator::And => 10, | ||||||
Operator::Like | Operator::NotLike => 19, | ||||||
Operator::NotEq | ||||||
| Operator::Eq | ||||||
| Operator::Lt | ||||||
| Operator::LtEq | ||||||
| Operator::Gt | ||||||
| Operator::GtEq => 20, | ||||||
Operator::Plus | Operator::Minus => 30, | ||||||
Operator::Multiply | Operator::Divide | Operator::Modulo => 40, | ||||||
_ => 0, | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
impl Display for BinaryExpr { | ||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | ||||||
// Put parentheses around child binary expressions so that we can see the difference | ||||||
// between `(a OR b) AND c` and `a OR (b AND c)`. We only insert parentheses when needed, | ||||||
// based on operator precedence. For example, `(a AND b) OR c` and `a AND b OR c` are | ||||||
// equivalent and the parentheses are not necessary. | ||||||
|
||||||
fn write_child( | ||||||
f: &mut Formatter<'_>, | ||||||
expr: &Expr, | ||||||
precedence: u8, | ||||||
) -> fmt::Result { | ||||||
match expr { | ||||||
Expr::BinaryExpr(child) => { | ||||||
let p = child.precedence(); | ||||||
if p == 0 || p < precedence { | ||||||
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.
Suggested change
Is the same? 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. Ah, good point |
||||||
write!(f, "({})", child)?; | ||||||
} else { | ||||||
write!(f, "{}", child)?; | ||||||
} | ||||||
} | ||||||
_ => write!(f, "{}", expr)?, | ||||||
} | ||||||
Ok(()) | ||||||
} | ||||||
|
||||||
let precedence = self.precedence(); | ||||||
write_child(f, self.left.as_ref(), precedence)?; | ||||||
write!(f, " {} ", self.op)?; | ||||||
write_child(f, self.right.as_ref(), precedence) | ||||||
} | ||||||
} | ||||||
|
||||||
/// CASE expression | ||||||
|
@@ -717,9 +769,7 @@ impl fmt::Debug for Expr { | |||||
negated: false, | ||||||
} => write!(f, "{:?} IN ({:?})", expr, subquery), | ||||||
Expr::ScalarSubquery(subquery) => write!(f, "({:?})", subquery), | ||||||
Expr::BinaryExpr(BinaryExpr { left, op, right }) => { | ||||||
write!(f, "{:?} {} {:?}", left, op, right) | ||||||
} | ||||||
Expr::BinaryExpr(expr) => write!(f, "{}", expr), | ||||||
Expr::Sort { | ||||||
expr, | ||||||
asc, | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -619,7 +619,7 @@ mod test { | |
)?; | ||
|
||
let expected = vec![ | ||
(9, "SUM(a + Int32(1)) - AVG(c) * Int32(2)Int32(2)SUM(a + Int32(1)) - AVG(c)AVG(c)cSUM(a + Int32(1))a + Int32(1)Int32(1)a"), | ||
(9, "(SUM(a + Int32(1)) - AVG(c)) * Int32(2)Int32(2)SUM(a + Int32(1)) - AVG(c)AVG(c)cSUM(a + Int32(1))a + Int32(1)Int32(1)a"), | ||
(7, "SUM(a + Int32(1)) - AVG(c)AVG(c)cSUM(a + Int32(1))a + Int32(1)Int32(1)a"), | ||
(4, "SUM(a + Int32(1))a + Int32(1)Int32(1)a"), | ||
(3, "a + Int32(1)Int32(1)a"), | ||
|
@@ -671,8 +671,8 @@ mod test { | |
)? | ||
.build()?; | ||
|
||
let expected = "Aggregate: groupBy=[[]], aggr=[[SUM(test.a * Int32(1) - test.bInt32(1) - test.btest.bInt32(1)test.a AS test.a * Int32(1) - test.b), SUM(test.a * Int32(1) - test.bInt32(1) - test.btest.bInt32(1)test.a AS test.a * Int32(1) - test.b * Int32(1) + test.c)]]\ | ||
\n Projection: test.a * Int32(1) - test.b AS test.a * Int32(1) - test.bInt32(1) - test.btest.bInt32(1)test.a, test.a, test.b, test.c\ | ||
let expected = "Aggregate: groupBy=[[]], aggr=[[SUM(test.a * (Int32(1) - test.b)Int32(1) - test.btest.bInt32(1)test.a AS test.a * Int32(1) - test.b), SUM(test.a * (Int32(1) - test.b)Int32(1) - test.btest.bInt32(1)test.a AS test.a * Int32(1) - test.b * (Int32(1) + test.c))]]\ | ||
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.
this is a little confused, should we change it to 🤔
Maybe we can file another issue 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. I think this is related to #3786 ? |
||
\n Projection: test.a * (Int32(1) - test.b) AS test.a * (Int32(1) - test.b)Int32(1) - test.btest.bInt32(1)test.a, test.a, test.b, test.c\ | ||
\n TableScan: test"; | ||
|
||
assert_optimized_plan_eq(expected, &plan); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 More clearly!