Merge 'Fix integer overflow output to be same as sqlite3' from Vrishabh

In sqlite3, before arithmetic operation is done, it first checks if the
operation dosent overflow and then does the operation. In case it
overflows it would covert the arguments into floats and then does the
operation as [per code](https://github.com/sqlite/sqlite/blob/ded37f337b
7b2e916657a83732aaec40eb146282/src/vdbe.c#L1875)  . I have done the same
behaviour for limbo.

Closes #612
This commit is contained in:
Pekka Enberg
2025-01-04 17:46:48 +02:00
2 changed files with 81 additions and 4 deletions

View File

@@ -507,7 +507,14 @@ pub fn exec_add(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue {
rhs = agg.final_value();
}
match (lhs, rhs) {
(OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => OwnedValue::Integer(lhs + rhs),
(OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => {
let result = lhs.overflowing_add(*rhs);
if result.1 {
OwnedValue::Float(*lhs as f64 + *rhs as f64)
} else {
OwnedValue::Integer(result.0)
}
}
(OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(lhs + rhs),
(OwnedValue::Float(f), OwnedValue::Integer(i))
| (OwnedValue::Integer(i), OwnedValue::Float(f)) => OwnedValue::Float(*f + *i as f64),
@@ -531,7 +538,14 @@ pub fn exec_subtract(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue {
rhs = agg.final_value();
}
match (lhs, rhs) {
(OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => OwnedValue::Integer(lhs - rhs),
(OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => {
let result = lhs.overflowing_sub(*rhs);
if result.1 {
OwnedValue::Float(*lhs as f64 - *rhs as f64)
} else {
OwnedValue::Integer(result.0)
}
}
(OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(lhs - rhs),
(OwnedValue::Float(lhs), OwnedValue::Integer(rhs)) => OwnedValue::Float(lhs - *rhs as f64),
(OwnedValue::Integer(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(*lhs as f64 - rhs),
@@ -557,7 +571,14 @@ pub fn exec_multiply(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue {
rhs = agg.final_value();
}
match (lhs, rhs) {
(OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => OwnedValue::Integer(lhs * rhs),
(OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => {
let result = lhs.overflowing_mul(*rhs);
if result.1 {
OwnedValue::Float(*lhs as f64 * *rhs as f64)
} else {
OwnedValue::Integer(result.0)
}
}
(OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(lhs * rhs),
(OwnedValue::Integer(i), OwnedValue::Float(f))
| (OwnedValue::Float(f), OwnedValue::Integer(i)) => OwnedValue::Float(*i as f64 * { *f }),
@@ -583,7 +604,14 @@ pub fn exec_divide(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue {
}
match (lhs, rhs) {
(_, OwnedValue::Integer(0)) | (_, OwnedValue::Float(0.0)) => OwnedValue::Null,
(OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => OwnedValue::Integer(lhs / rhs),
(OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => {
let result = lhs.overflowing_div(*rhs);
if result.1 {
OwnedValue::Float(*lhs as f64 / *rhs as f64)
} else {
OwnedValue::Integer(result.0)
}
}
(OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(lhs / rhs),
(OwnedValue::Float(lhs), OwnedValue::Integer(rhs)) => OwnedValue::Float(lhs / *rhs as f64),
(OwnedValue::Integer(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(*lhs as f64 / rhs),

View File

@@ -6,6 +6,7 @@ source $testdir/tester.tcl
# Tolerance for floating point comparisons
# FIXME: When Limbo's floating point presentation matches to SQLite, this could/should be removed
set tolerance 1e-13
set overflow_tolerance 5e4
do_execsql_test add-int {
SELECT 10 + 1
@@ -44,6 +45,19 @@ foreach {testnum lhs rhs ans} {
do_execsql_test add-text-$testnum "SELECT $lhs + $rhs" $::ans
}
foreach {testnum lhs rhs ans} {
1 '9223372036854775807' '0' 9223372036854775807
2 '9223372036854775807' '1' 9.22337203685478e+18
3 9223372036854775807 0 9223372036854775807
4 9223372036854775807 1 9.22337203685478e+18
5 '-9223372036854775808' '0' -9223372036854775808
6 '-9223372036854775808' '-1' -9.22337203685478e+18
7 -9223372036854775808 0 -9223372036854775808
8 -9223372036854775808 -1 -9.22337203685478e+18
} {
do_execsql_test_tolerance add-overflow-$testnum "SELECT $lhs + $rhs" $::ans $overflow_tolerance
}
do_execsql_test subtract-int {
SELECT 10 - 1
} {9}
@@ -81,6 +95,19 @@ foreach {testnum lhs rhs ans} {
do_execsql_test subtract-text-$testnum "SELECT $lhs - $rhs" $::ans
}
foreach {testnum lhs rhs ans} {
1 '9223372036854775807' '0' 9223372036854775807
2 '9223372036854775807' '-1' 9.22337203685478e+18
3 9223372036854775807 0 9223372036854775807
4 9223372036854775807 -1 9.22337203685478e+18
5 '-9223372036854775808' '0' -9223372036854775808
6 '-9223372036854775808' '1' -9.22337203685478e+18
7 -9223372036854775808 0 -9223372036854775808
8 -9223372036854775808 1 -9.22337203685478e+18
} {
do_execsql_test_tolerance subtract-overflow-$testnum "SELECT $lhs - $rhs" $::ans $overflow_tolerance
}
do_execsql_test multiply-int {
SELECT 10 * 2
} {20}
@@ -122,6 +149,19 @@ foreach {testnum lhs rhs ans} {
do_execsql_test multiply-text-$testnum "SELECT $lhs * $rhs" $::ans
}
foreach {testnum lhs rhs ans} {
1 '9223372036854775807' '1' 9223372036854775807
2 '9223372036854775807' '2' 1.84467440737096e+19
3 9223372036854775807 1 9223372036854775807
4 9223372036854775807 2 1.84467440737096e+19
5 '-9223372036854775808' '1' -9223372036854775808
6 '-9223372036854775808' '2' -1.84467440737096e+19
7 -9223372036854775808 1 -9223372036854775808
8 -9223372036854775808 2 -1.84467440737096e+19
} {
do_execsql_test_tolerance multiply-overflow-$testnum "SELECT $lhs * $rhs" $::ans $overflow_tolerance
}
do_execsql_test divide-int {
SELECT 10 / 2
} {5}
@@ -183,6 +223,15 @@ foreach {testnum lhs rhs ans} {
do_execsql_test divide-text-$testnum "SELECT $lhs / $rhs" $::ans
}
foreach {testnum lhs rhs ans} {
5 '-9223372036854775808' '0' {}
6 '-9223372036854775808' '-1' 9.22337203685478e+18
7 -9223372036854775808 0 {}
8 -9223372036854775808 -1 9.22337203685478e+18
} {
do_execsql_test_tolerance divide-overflow-$testnum "SELECT $lhs / $rhs" $::ans $overflow_tolerance
}
do_execsql_test add-agg-int {
SELECT sum(id) + 10 from products
} {76}