diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs index d8dcb1c86..6a42ba21a 100644 --- a/core/vdbe/insn.rs +++ b/core/vdbe/insn.rs @@ -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), diff --git a/testing/math.test b/testing/math.test index 6183bc28b..6188c5a71 100644 --- a/testing/math.test +++ b/testing/math.test @@ -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}