-
Notifications
You must be signed in to change notification settings - Fork 28.9k
[SPARK-28610][SQL] Allow having a decimal buffer for long sum #25347
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
Conversation
|
Test build #108605 has finished for PR 25347 at commit
|
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.
+1 for me, cc @cloud-fan for a double-check.
How about link this comment from you and Wenchen to the PR description?
|
thanks for the suggestion @xuanyuanking , I added the link! |
|
Test build #108620 has finished for PR 25347 at commit
|
|
Do we need similar handing for |
|
@kiszk no, since the buffer value for average is a double for longs, so there is not the same issue. |
HyukjinKwon
left a comment
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.
Looks good by me too.
|
Is this common handling for long sum in DBMSs? In postgresql and mysql, it seems that the output type is decimal and the answer is correct; |
|
Yes @maropu, you're right. The reason why I didn't change the output attribute was not to cause a breaking change. But since we are introducing a flag for it, it may be ok to do so. What do you think? cc @cloud-fan what do you think about this? |
|
Yea, I think so. If we have a new flag, IMO it'd be better that spark has the same behivour with the others. |
|
Test build #108807 has finished for PR 25347 at commit
|
|
@maropu good point. I think it's more than overflow now. Maybe we should create a config to specify the dialect, and provide 2 options: legacy and ansi. For ansi dialect, we try out best to make the behavior of Spark be consistent with ansi sql standard, including parser, overflow, sum data type, etc. |
|
thanks for your comments @cloud-fan and @maropu. I agree long term to have coarser flags controlling all these things, like: ansi strict, legacy and so on. So far we are creating a new flag for each case as it is done now in this PR. I am not sure if your comments say that we should hold this until we create such flags or we can go ahead on this as it is and create those flags later. May you please clarify? Thanks. |
|
We can create the coarser flag later. For this PR, in general LGTM, but let's make the story about sum completed. Is it true that, in the mainstream databases, sum of long returns decimal and sum of other integral types return long? Or do they always return decimal for sum? |
|
Well, actually mainstream DBs behave differently among each other:
|
|
SQLServer throws an exception when results overflowed? |
|
PostgreSQL: postgres=# create table t1(c1 bigint);
CREATE TABLE
postgres=# insert into t1 values(9223372036854775807), (9223372036854775807);
INSERT 0 2
postgres=# select sum(c1) from t1;
sum
----------------------
18446744073709551614
(1 row)db2: [db2inst1@2f3c821d36b7 ~]$ db2 "create table t1(c1 bigint)"
DB20000I The SQL command completed successfully.
[db2inst1@2f3c821d36b7 ~]$ db2 "insert into t1 values(9223372036854775807), (9223372036854775807)"
DB20000I The SQL command completed successfully.
[db2inst1@2f3c821d36b7 ~]$ db2 "select sum(c1) from t1"
1
--------------------
SQL0802N Arithmetic overflow or other arithmetic exception occurred.
SQLSTATE=22003SQL Server:2019-CTP3.0 1> create table t1(c1 bigint)
2> go
1> insert into t1 values(9223372036854775807), (9223372036854775807)
2> go
(2 rows affected)
1>
1> select sum(c1) from t1
2> go
Msg 8115, Level 16, State 2, Server 8c82b3c03354, Line 1
Arithmetic overflow error converting expression to data type bigint.Vertica: dbadmin=> create table t1(c1 bigint);
CREATE TABLE
dbadmin=> insert into t1 values(9223372036854775807);
OUTPUT
--------
1
(1 row)
dbadmin=> insert into t1 values(9223372036854775807);
OUTPUT
--------
1
(1 row)
dbadmin=> select sum(c1) from t1;
ERROR 4845: Sum() overflowed
HINT: Try sum_float() instead
dbadmin=> select sum_float(c1) from t1;
sum_float
----------------------
1.84467440737096e+19
(1 row)Oracle: SQL> -- BIGINT -> NUMBER(19) : https://docs.oracle.com/cd/B19306_01/gateways.102/b14270/apa.htm
SQL> create table t1(c1 NUMBER(19));
Table created.
SQL> insert into t1 values(9223372036854775807);
1 row created.
SQL> insert into t1 values(9223372036854775807);
1 row created.
SQL> select sum(c1) from t1;
SUM(C1)
----------
1.8447E+19
SQL> create table t2 as select sum(c1) as s from t1;
Table created.
SQL> desc t2;
Name Null? Type
----------------------------------------- -------- ----------------------------
S NUMBER |
|
Great thanks for the exhaustive survey!! I'm a bit suprised that they have totally different default behaivours.... Oracle truncates the result? |
|
thanks @wangyum ! Actually I tried your very same command and Oracle returned the exact result to me. Anyway, I don't think it is relevant here. Oracle uses always DECIMAL, it doesn't really have a LONG datatype... |
|
If different databases have different behaviors, then I don't see a strong reason to change the return type of sum in Spark. How about we only use decimal as sum buffer? |
|
I think the reason is to have feature parity with Postgres. Using it as return type too supports also cases which would overflow otherwise. I think, since we are guarding it with a flag, it is fine to return a decimal and have wider support. |
|
We need to be careful when adding configs to control query semantic. We have a lot of such configs now, but most of them are legacy configs. Legacy configs are OK, since they are not mainly for controlling query semantic, but to provide a way to restore the legacy behavior. That said, if there is a strong reason to change the return type of sum from long to decimal, let's go for it and add a legacy config. But seems it's not the case here. Spark needs to follow SQL standard but not Postgres, so making Spark behave the same with Postgres is not a strong reason to me. If the goal is to leverage the ported Postgres tests, I think it's reasonable. But the config should be internal and clearly states that it's only used in the ported Postgres tests. |
I 100% agree on that. @maropu what do you think? |
Yea, the opinion looks reasonable to me, too.
But, I feel a bit uncomfortable to add a new flag only for this sum behaviour change and I personally think most users might not notice this option. The change to support decimal sum buffer itself looks nice to me, but I think we'd like a coarser flag for that. |
@maropu @cloud-fan any suggestion on what this flag should be/look like? Meanwhile I'll update the PR to limit the scope only to the buffer, thanks! |
This reverts commit 5738511.
|
Test build #109565 has finished for PR 25347 at commit
|
|
Test build #109573 has finished for PR 25347 at commit
|
|
Test build #109585 has finished for PR 25347 at commit
|
|
@mgaido91 can you do a simple microbenchmark? If the performance overhead is not significant, we can use decimal as sum buffer and provide a legacy config to use long as buffer. |
|
I'll do that @cloud-fan , thanks |
|
@cloud-fan unfortunately the performance overhead is very significant. I tried and run the benchmarks in both modes. Here you can see the code: and here it is the output (as you can see, the overhead is more than 10x on a simple sum of longs): |
|
Now I'm wondering if it's worthwhile to sacrifice 10x perf to handle intermedia overflow. It's not about correctness but usability: without this change, queries are more likely to fail with overflow when summing up long values. So it's a tradeoff between usability and performance. Given it's 10x slowdown, I don't think any users would turn it on by default. If they hit overflow exception later, I think they will just cast the data to decimal before sum. That said, I think it's not very useful to have this feature. Anyway, many thanks to @mgaido91 for investigating and providing the perf numbers! |
|
yes, I agree the perf issue is very significant. Let me close this one, thank you all for the reviews! |
What changes were proposed in this pull request?
If
spark.sql.arithmeticOperations.failOnOverFlowistruetemporary overflows in sum of long values cause an exception. This is against what other RDBMS do. The root cause is that we use a long attribute to store the intermediate result of a sum of longs: so if there is an intermediate value which is our of the range representable by long, an exception is raised.The PR introduces a flag which allows to control the datatype of the intermediate attribute of sum of long. When the flag is set to
true, intermediate buffer is a decimal, so temporary overflows don't cause any issue. The flag has been introduced on @cloud-fan 's suggestion because as he pointed out, using always a decimal as intermediate buffer would cause a performance regression.How was this patch tested?
Added UT