Repository navigation
Unfold the round function in logical plan if it has 2 arguments - #4234
HaoYang670 wants to merge 4 commits into
Conversation
Signed-off-by: remzi <13716567376yh@gmail.com>
| println!("I am here, source = {:?}, n = {:?}", source, n); | ||
| let exp = Box::new(power( | ||
| lit(10.0), | ||
| Expr::Cast(Cast::new(n.clone().into(), DataType::Float64)), |
There was a problem hiding this comment.
One problem here is that we will always convert the data type to Float64 to adapt the power function
|
Also, I didn't want to put this logic in the optimizer. Because it doesn't optimize the plan, but convert some syntax sugar. I plan to create a new stage calling "desugar_expression" and put it in front of the optimizer, if it is necessary. |
round function in logical plan if it has 2 argumentsround function in logical plan if it has 2 arguments
Signed-off-by: remzi <13716567376yh@gmail.com>
| let sql = "SELECT round(col_f32, col_int32) as col from test"; | ||
| let plan = test_sql(sql)?; | ||
| let expected = | ||
| "Projection: round(CAST(test.col_f32 AS Float64) * power(Float64(10), CAST(CAST(test.col_int32 AS Int64) AS Float64))CAST(CAST(test.col_int32 AS Int64) AS Float64)CAST(test.col_int32 AS Int64)test.col_int32Float64(10) AS power(Float64(10),test.col_int32)) / power(Float64(10), CAST(CAST(test.col_int32 AS Int64) AS Float64))CAST(CAST(test.col_int32 AS Int64) AS Float64)CAST(test.col_int32 AS Int64)test.col_int32Float64(10) AS power(Float64(10),test.col_int32) AS col\ |
There was a problem hiding this comment.
The lots of Cast here is a little weird, I am looking into it.
| let sql = "SELECT round(col_f32, 2) as col from test"; | ||
| let plan = test_sql(sql)?; | ||
| let expected = | ||
| "Projection: round(CAST(test.col_f32 AS Float64) * Float64(100)) / Float64(100) AS col\ |
There was a problem hiding this comment.
We can benefit from the expression simplification when one of the argument is a constant
|
@andygrove Could you please help to review? |
| // unfold the expression round(source, n) | ||
| Expr::ScalarFunction { | ||
| fun: BuiltinScalarFunction::Round, | ||
| args, |
There was a problem hiding this comment.
I would prefer that we don't re-write this in the logical plan to work around the fact that we don't currently support this in the physical plan. This makes the logical plan harder to use for other query engines, such as Dask SQL, that are using DataFusion for SQL query planning.
I suggest for this PR that we throw an error in the physical query planner if we see round with two arguments. This would be better than producing incorrect results. We can implement the functionality in the physical plan in a separate PR.
There was a problem hiding this comment.
This makes the logical plan harder to use for other query engines
Why is that? Could you please give an example?
We can implement the functionality in the physical plan in a separate PR.
The advantages of implementing in physical plan are:
- much straight forward and easy to debug.
- we would benefit from arrow-rs, if arrow-rs could support
round(source, n)in the future.
The disadvantages of implementing in physical plan are:
- Rust only support the
roundfunction with one argument. If we want to implement in physical plan, we have to assemble some arrow compute kernel together, which the logic is same as what I've done in the physical plan. - We can't optimize the expression if the second function is a constant, which we have to add additional optimize rules.
The advantages of implementing in logical plan are:
- Reuse other code to achieve the functionality of
round(source, n), so the implementation could be much easier. - We can reuse our expression optimizer to do optimizations, for example when the 2nd argument is a constant.
- Flexible. If we plan to change the parameter type of
roundfromFloat, toDecimal, the change can be very easy. - Make logical stage and physical stage independent. We can achieve complex functionality in the SQL query by unfolding and rewriting expressions (or plans) in the logical stage and we only need to maintain a reduced set of physical expressions (or plans).
The disadvantages of implementing in logical plan are:
- Put the implementation in the logical optimizer isn't a good solution, we should unfold the expression before doing any optimization.
- the query plan isn't user-friendly to read because
roundis unfolded. But I think this is not a big deal, as the optimized code is always ugly to read. Users can also useexplain verboseto find what has happened.
After weighing the pros and cons, I prefer to implement in logical plan. If the rust lib or arrow-rs could support round(source, n), in the future, we can change it.
There was a problem hiding this comment.
Why is that? Could you please give an example?
Sure. Taking Dask SQL as an example, we would want to translate round(a, b) to a call to a Python function to perform the rounding (perhaps in numpy). With this PR, the logical plan now has a complex expression that is specific to the DataFusion implementation. Dask SQL would have to undo the pow logic here to get back to the original parameters to round so that it can pass them to numpy.
Projection: round(CAST(test.column_1 AS Float64) * Float64(0.01)) / Float64(0.01) AS round(test.column_1,Int64(-2))
There was a problem hiding this comment.
With this PR, the logical plan now has a complex expression that is specific to the DataFusion implementation. Dask SQL would have to undo the pow logic here to get back to the original parameters to round so that it can pass them to numpy.
If the dask-sql has a physical execution to support round(source, n), this could be a problem. 🤔
the logical plan now has a complex expression that is specific to the DataFusion implementation
This is a little weird for me that the Datafusion logical plan should not be specific to Datafusion physical plan.
If this is the truth, why don't we seperate the Datafusion to be two projects?
|
Closing as this PR is over a year old. Please feel free to reopen it / rebase it if you plan to keep working on it |
Signed-off-by: remzi 13716567376yh@gmail.com
Which issue does this PR close?
Closes #4191.
Rationale for this change
Before the change
After the change
What changes are included in this PR?
Unfold the expression
round(source, n)asround(source * pow(10, n)) / pow(10, n)in the logical plan.Here are some reasons that I don't implement
round(source, n)in the physical expression, but in logical stage:roundcontains literal argumentsAre these changes tested?
Yes.
Are there any user-facing changes?