mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-10 11:44:22 +01:00
Merge 'Improve maths support' from Lauri Virtanen
- Add support for division in SQL expressions - Fix issues with subtraction - Support multiplication of integers and floats - Support aggregate functions in mathematical expressions - Add compatibility tests for mathematical operations, also with aggregate functions Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com> Closes #418
This commit is contained in:
@@ -236,7 +236,7 @@ This document describes the SQLite compatibility status of Limbo:
|
||||
| DecrJumpZero | Yes |
|
||||
| Delete | No |
|
||||
| Destroy | No |
|
||||
| Divide | No |
|
||||
| Divide | Yes |
|
||||
| DropIndex | No |
|
||||
| DropTable | No |
|
||||
| DropTrigger | No |
|
||||
@@ -286,7 +286,7 @@ This document describes the SQLite compatibility status of Limbo:
|
||||
| MaxPgcnt | No |
|
||||
| MemMax | No |
|
||||
| Move | No |
|
||||
| Multiply | No |
|
||||
| Multiply | Yes |
|
||||
| MustBeInt | Yes |
|
||||
| Ne | Yes |
|
||||
| NewRowid | Yes |
|
||||
@@ -355,7 +355,7 @@ This document describes the SQLite compatibility status of Limbo:
|
||||
| SorterSort | Yes |
|
||||
| String | No |
|
||||
| String8 | Yes |
|
||||
| Subtract | No |
|
||||
| Subtract | Yes |
|
||||
| TableLock | No |
|
||||
| ToBlob | No |
|
||||
| ToInt | No |
|
||||
|
||||
@@ -720,6 +720,13 @@ pub fn translate_expr(
|
||||
dest: target_register,
|
||||
});
|
||||
}
|
||||
ast::Operator::Divide => {
|
||||
program.emit_insn(Insn::Divide {
|
||||
lhs: e1_reg,
|
||||
rhs: e2_reg,
|
||||
dest: target_register,
|
||||
});
|
||||
}
|
||||
other_unimplemented => todo!("{:?}", other_unimplemented),
|
||||
}
|
||||
Ok(target_register)
|
||||
|
||||
@@ -46,6 +46,15 @@ pub fn insn_to_str(
|
||||
0,
|
||||
format!("r[{}]=r[{}]*r[{}]", dest, lhs, rhs),
|
||||
),
|
||||
Insn::Divide { lhs, rhs, dest } => (
|
||||
"Divide",
|
||||
*lhs as i32,
|
||||
*rhs as i32,
|
||||
*dest as i32,
|
||||
OwnedValue::Text(Rc::new("".to_string())),
|
||||
0,
|
||||
format!("r[{}]=r[{}]/r[{}]", dest, lhs, rhs),
|
||||
),
|
||||
Insn::Null { dest, dest_end } => (
|
||||
"Null",
|
||||
0,
|
||||
|
||||
255
core/vdbe/mod.rs
255
core/vdbe/mod.rs
@@ -107,6 +107,12 @@ pub enum Insn {
|
||||
rhs: usize,
|
||||
dest: usize,
|
||||
},
|
||||
// Divide lhs by rhs and store the result in a third register.
|
||||
Divide {
|
||||
lhs: usize,
|
||||
rhs: usize,
|
||||
dest: usize,
|
||||
},
|
||||
// Compare two vectors of registers in reg(P1)..reg(P1+P3-1) (call this vector "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of the comparison for use by the next Jump instruct.
|
||||
Compare {
|
||||
start_reg_a: usize,
|
||||
@@ -724,43 +730,94 @@ impl Program {
|
||||
(OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => {
|
||||
state.registers[dest] = OwnedValue::Float(lhs - rhs);
|
||||
}
|
||||
(OwnedValue::Float(lhs), OwnedValue::Integer(rhs))
|
||||
| (OwnedValue::Integer(rhs), OwnedValue::Float(lhs)) => {
|
||||
(OwnedValue::Float(lhs), OwnedValue::Integer(rhs)) => {
|
||||
state.registers[dest] = OwnedValue::Float(lhs - *rhs as f64);
|
||||
}
|
||||
(OwnedValue::Integer(lhs), OwnedValue::Float(rhs)) => {
|
||||
state.registers[dest] = OwnedValue::Float(*lhs as f64 - rhs);
|
||||
}
|
||||
(OwnedValue::Null, _) | (_, OwnedValue::Null) => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
(OwnedValue::Agg(aggctx), other) | (other, OwnedValue::Agg(aggctx)) => {
|
||||
match other {
|
||||
OwnedValue::Null => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
OwnedValue::Integer(i) => match aggctx.final_value() {
|
||||
OwnedValue::Float(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(acc - *i as f64);
|
||||
}
|
||||
OwnedValue::Integer(acc) => {
|
||||
state.registers[dest] = OwnedValue::Integer(acc - i);
|
||||
}
|
||||
_ => {
|
||||
todo!("{:?}", aggctx);
|
||||
}
|
||||
},
|
||||
OwnedValue::Float(f) => match aggctx.final_value() {
|
||||
OwnedValue::Float(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(acc - f);
|
||||
}
|
||||
OwnedValue::Integer(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(*acc as f64 - f);
|
||||
}
|
||||
_ => {
|
||||
todo!("{:?}", aggctx);
|
||||
}
|
||||
},
|
||||
rest => unimplemented!("{:?}", rest),
|
||||
(OwnedValue::Agg(aggctx), rhs) => match rhs {
|
||||
OwnedValue::Null => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
}
|
||||
OwnedValue::Integer(i) => match aggctx.final_value() {
|
||||
OwnedValue::Float(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(acc - *i as f64);
|
||||
}
|
||||
OwnedValue::Integer(acc) => {
|
||||
state.registers[dest] = OwnedValue::Integer(acc - i);
|
||||
}
|
||||
_ => {
|
||||
todo!("{:?}", aggctx);
|
||||
}
|
||||
},
|
||||
OwnedValue::Float(f) => match aggctx.final_value() {
|
||||
OwnedValue::Float(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(acc - f);
|
||||
}
|
||||
OwnedValue::Integer(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(*acc as f64 - f);
|
||||
}
|
||||
_ => {
|
||||
todo!("{:?}", aggctx);
|
||||
}
|
||||
},
|
||||
OwnedValue::Agg(aggctx2) => {
|
||||
let acc = aggctx.final_value();
|
||||
let acc2 = aggctx2.final_value();
|
||||
match (acc, acc2) {
|
||||
(OwnedValue::Integer(acc), OwnedValue::Integer(acc2)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(acc - acc2);
|
||||
}
|
||||
(OwnedValue::Float(acc), OwnedValue::Float(acc2)) => {
|
||||
state.registers[dest] = OwnedValue::Float(acc - acc2);
|
||||
}
|
||||
(OwnedValue::Integer(acc), OwnedValue::Float(acc2)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Float(*acc as f64 - acc2);
|
||||
}
|
||||
(OwnedValue::Float(acc), OwnedValue::Integer(acc2)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Float(acc - *acc2 as f64);
|
||||
}
|
||||
_ => {
|
||||
todo!("{:?} {:?}", acc, acc2);
|
||||
}
|
||||
}
|
||||
}
|
||||
rest => unimplemented!("{:?}", rest),
|
||||
},
|
||||
(lhs, OwnedValue::Agg(aggctx)) => match lhs {
|
||||
OwnedValue::Null => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
OwnedValue::Integer(i) => match aggctx.final_value() {
|
||||
OwnedValue::Float(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(*i as f64 - acc);
|
||||
}
|
||||
OwnedValue::Integer(acc) => {
|
||||
state.registers[dest] = OwnedValue::Integer(i - acc);
|
||||
}
|
||||
_ => {
|
||||
todo!("{:?}", aggctx);
|
||||
}
|
||||
},
|
||||
OwnedValue::Float(f) => match aggctx.final_value() {
|
||||
OwnedValue::Float(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(f - acc);
|
||||
}
|
||||
OwnedValue::Integer(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(f - *acc as f64);
|
||||
}
|
||||
_ => {
|
||||
todo!("{:?}", aggctx);
|
||||
}
|
||||
},
|
||||
rest => unimplemented!("{:?}", rest),
|
||||
},
|
||||
others => {
|
||||
todo!("{:?}", others);
|
||||
}
|
||||
@@ -778,6 +835,10 @@ impl Program {
|
||||
(OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => {
|
||||
state.registers[dest] = OwnedValue::Float(lhs * rhs);
|
||||
}
|
||||
(OwnedValue::Integer(i), OwnedValue::Float(f))
|
||||
| (OwnedValue::Float(f), OwnedValue::Integer(i)) => {
|
||||
state.registers[dest] = OwnedValue::Float(*i as f64 * *f as f64);
|
||||
}
|
||||
(OwnedValue::Null, _) | (_, OwnedValue::Null) => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
@@ -808,6 +869,29 @@ impl Program {
|
||||
todo!("{:?}", aggctx);
|
||||
}
|
||||
},
|
||||
OwnedValue::Agg(aggctx2) => {
|
||||
let acc = aggctx.final_value();
|
||||
let acc2 = aggctx2.final_value();
|
||||
match (acc, acc2) {
|
||||
(OwnedValue::Integer(acc), OwnedValue::Integer(acc2)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(acc * acc2);
|
||||
}
|
||||
(OwnedValue::Float(acc), OwnedValue::Float(acc2)) => {
|
||||
state.registers[dest] = OwnedValue::Float(acc * acc2);
|
||||
}
|
||||
(OwnedValue::Integer(acc), OwnedValue::Float(acc2)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Float(*acc as f64 * acc2);
|
||||
}
|
||||
(OwnedValue::Float(acc), OwnedValue::Integer(acc2)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Float(acc * *acc2 as f64);
|
||||
}
|
||||
_ => {
|
||||
todo!("{:?} {:?}", acc, acc2);
|
||||
}
|
||||
}
|
||||
}
|
||||
rest => unimplemented!("{:?}", rest),
|
||||
}
|
||||
}
|
||||
@@ -817,6 +901,115 @@ impl Program {
|
||||
}
|
||||
state.pc += 1;
|
||||
}
|
||||
Insn::Divide { lhs, rhs, dest } => {
|
||||
let lhs = *lhs;
|
||||
let rhs = *rhs;
|
||||
let dest = *dest;
|
||||
match (&state.registers[lhs], &state.registers[rhs]) {
|
||||
// If the divisor is zero, the result is NULL.
|
||||
(_, OwnedValue::Integer(0)) | (_, OwnedValue::Float(0.0)) => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
(OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(lhs / rhs);
|
||||
}
|
||||
(OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => {
|
||||
state.registers[dest] = OwnedValue::Float(lhs / rhs);
|
||||
}
|
||||
(OwnedValue::Float(lhs), OwnedValue::Integer(rhs)) => {
|
||||
state.registers[dest] = OwnedValue::Float(lhs / *rhs as f64);
|
||||
}
|
||||
(OwnedValue::Integer(lhs), OwnedValue::Float(rhs)) => {
|
||||
state.registers[dest] = OwnedValue::Float(*lhs as f64 / rhs);
|
||||
}
|
||||
(OwnedValue::Null, _) | (_, OwnedValue::Null) => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
(OwnedValue::Agg(aggctx), rhs) => match rhs {
|
||||
OwnedValue::Null => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
OwnedValue::Integer(i) => match aggctx.final_value() {
|
||||
OwnedValue::Float(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(acc / *i as f64);
|
||||
}
|
||||
OwnedValue::Integer(acc) => {
|
||||
state.registers[dest] = OwnedValue::Integer(acc / i);
|
||||
}
|
||||
_ => {
|
||||
todo!("{:?}", aggctx);
|
||||
}
|
||||
},
|
||||
OwnedValue::Float(f) => match aggctx.final_value() {
|
||||
OwnedValue::Float(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(acc / f);
|
||||
}
|
||||
OwnedValue::Integer(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(*acc as f64 / f);
|
||||
}
|
||||
_ => {
|
||||
todo!("{:?}", aggctx);
|
||||
}
|
||||
},
|
||||
OwnedValue::Agg(aggctx2) => {
|
||||
let acc = aggctx.final_value();
|
||||
let acc2 = aggctx2.final_value();
|
||||
match (acc, acc2) {
|
||||
(OwnedValue::Integer(acc), OwnedValue::Integer(acc2)) => {
|
||||
state.registers[dest] = OwnedValue::Integer(acc / acc2);
|
||||
}
|
||||
(OwnedValue::Float(acc), OwnedValue::Float(acc2)) => {
|
||||
state.registers[dest] = OwnedValue::Float(acc / acc2);
|
||||
}
|
||||
(OwnedValue::Integer(acc), OwnedValue::Float(acc2)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Float(*acc as f64 / acc2);
|
||||
}
|
||||
(OwnedValue::Float(acc), OwnedValue::Integer(acc2)) => {
|
||||
state.registers[dest] =
|
||||
OwnedValue::Float(acc / *acc2 as f64);
|
||||
}
|
||||
_ => {
|
||||
todo!("{:?} {:?}", acc, acc2);
|
||||
}
|
||||
}
|
||||
}
|
||||
rest => unimplemented!("{:?}", rest),
|
||||
},
|
||||
(lhs, OwnedValue::Agg(aggctx)) => match lhs {
|
||||
OwnedValue::Null => {
|
||||
state.registers[dest] = OwnedValue::Null;
|
||||
}
|
||||
OwnedValue::Integer(i) => match aggctx.final_value() {
|
||||
OwnedValue::Float(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(*i as f64 / acc);
|
||||
}
|
||||
OwnedValue::Integer(acc) => {
|
||||
state.registers[dest] = OwnedValue::Integer(i / acc);
|
||||
}
|
||||
_ => {
|
||||
todo!("{:?}", aggctx);
|
||||
}
|
||||
},
|
||||
OwnedValue::Float(f) => match aggctx.final_value() {
|
||||
OwnedValue::Float(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(f / acc);
|
||||
}
|
||||
OwnedValue::Integer(acc) => {
|
||||
state.registers[dest] = OwnedValue::Float(f / *acc as f64);
|
||||
}
|
||||
_ => {
|
||||
todo!("{:?}", aggctx);
|
||||
}
|
||||
},
|
||||
rest => unimplemented!("{:?}", rest),
|
||||
},
|
||||
others => {
|
||||
todo!("{:?}", others);
|
||||
}
|
||||
}
|
||||
state.pc += 1;
|
||||
}
|
||||
Insn::Null { dest, dest_end } => {
|
||||
if let Some(dest_end) = dest_end {
|
||||
for i in *dest..=*dest_end {
|
||||
|
||||
@@ -10,6 +10,7 @@ source $testdir/join.test
|
||||
source $testdir/insert.test
|
||||
source $testdir/json.test
|
||||
source $testdir/like.test
|
||||
source $testdir/math.test
|
||||
source $testdir/orderby.test
|
||||
source $testdir/groupby.test
|
||||
source $testdir/pragma.test
|
||||
|
||||
216
testing/math.test
Normal file
216
testing/math.test
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env tclsh
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_execsql_test add-int {
|
||||
SELECT 10 + 1
|
||||
} {11}
|
||||
|
||||
do_execsql_test add-float {
|
||||
SELECT 10.1 + 0.3
|
||||
} {10.4}
|
||||
|
||||
do_execsql_test add-int-float {
|
||||
SELECT 10 + 0.1
|
||||
} {10.1}
|
||||
|
||||
do_execsql_test subtract-int {
|
||||
SELECT 10 - 1
|
||||
} {9}
|
||||
|
||||
do_execsql_test subtract-float {
|
||||
SELECT 10.2 - 0.1
|
||||
} {10.1}
|
||||
|
||||
do_execsql_test subtract-int-float {
|
||||
SELECT 10 - 0.1
|
||||
} {9.9}
|
||||
|
||||
do_execsql_test multiply-int {
|
||||
SELECT 10 * 2
|
||||
} {20}
|
||||
|
||||
do_execsql_test multiply-float {
|
||||
SELECT 10.2 * 2.2
|
||||
} {22.44}
|
||||
|
||||
do_execsql_test multiply-int-float {
|
||||
SELECT 10 * 1.45
|
||||
} {14.5}
|
||||
|
||||
do_execsql_test multiply-float-int {
|
||||
SELECT 1.45 * 10
|
||||
} {14.5}
|
||||
|
||||
do_execsql_test divide-int {
|
||||
SELECT 10 / 2
|
||||
} {5}
|
||||
|
||||
do_execsql_test divide-int-no-fraction {
|
||||
SELECT 10 / 3
|
||||
} {3}
|
||||
|
||||
do_execsql_test divide-float {
|
||||
SELECT 10.6 / 2.5
|
||||
} {4.24}
|
||||
|
||||
do_execsql_test divide-int-float {
|
||||
SELECT 10 / 4.0
|
||||
} {2.5}
|
||||
|
||||
do_execsql_test divide-float-int {
|
||||
SELECT 10.0 / 4
|
||||
} {2.5}
|
||||
|
||||
do_execsql_test divide-by-zero {
|
||||
SELECT 10 / 0
|
||||
} {}
|
||||
|
||||
do_execsql_test divide-int-null {
|
||||
SELECT 10 / null
|
||||
} {}
|
||||
|
||||
do_execsql_test divide-null-int {
|
||||
SELECT null / 10
|
||||
} {}
|
||||
|
||||
do_execsql_test divide-null {
|
||||
SELECT null / null
|
||||
} {}
|
||||
|
||||
|
||||
|
||||
do_execsql_test add-agg-int {
|
||||
SELECT sum(id) + 10 from products
|
||||
} {76}
|
||||
|
||||
do_execsql_test add-int-agg {
|
||||
SELECT 10 + sum(id) from products
|
||||
} {76}
|
||||
|
||||
do_execsql_test add-agg-float {
|
||||
SELECT sum(id) + 10.1 from products
|
||||
} {76.1}
|
||||
|
||||
do_execsql_test add-float-agg {
|
||||
SELECT 10.1 + sum(id) from products
|
||||
} {76.1}
|
||||
|
||||
do_execsql_test add-agg-int-agg-int {
|
||||
SELECT sum(id) + sum(id) from products
|
||||
} {132}
|
||||
|
||||
do_execsql_test add-agg-float-agg-float {
|
||||
SELECT sum(price) + sum(price) from products
|
||||
} {1246.0}
|
||||
|
||||
do_execsql_test add-agg-int-agg-float {
|
||||
SELECT sum(id) + sum(price) from products
|
||||
} {689.0}
|
||||
|
||||
do_execsql_test add-agg-int-agg-float {
|
||||
SELECT sum(id) + sum(price) from products
|
||||
} {689.0}
|
||||
|
||||
|
||||
|
||||
do_execsql_test subtract-agg-int {
|
||||
SELECT sum(id) - 10 from products
|
||||
} {56}
|
||||
|
||||
do_execsql_test subtract-int-agg {
|
||||
SELECT 10 - sum(id) from products
|
||||
} {-56}
|
||||
|
||||
do_execsql_test subtract-agg-float {
|
||||
SELECT sum(id) - 10.1 from products
|
||||
} {55.9}
|
||||
|
||||
do_execsql_test subtract-float-agg {
|
||||
SELECT 10.1 - sum(id) from products
|
||||
} {-55.9}
|
||||
|
||||
do_execsql_test subtract-agg-int-agg-int {
|
||||
SELECT sum(id) - sum(id) from products
|
||||
} {0}
|
||||
|
||||
do_execsql_test subtract-agg-float-agg-float {
|
||||
SELECT sum(price) - sum(price) from products
|
||||
} {0.0}
|
||||
|
||||
do_execsql_test subtract-agg-int-agg-float {
|
||||
SELECT sum(id) - sum(price) from products
|
||||
} {-557.0}
|
||||
|
||||
do_execsql_test subtract-agg-float-agg-int {
|
||||
SELECT sum(price) - sum(id) from products
|
||||
} {557.0}
|
||||
|
||||
|
||||
|
||||
do_execsql_test multiply-agg-int {
|
||||
SELECT sum(id) * 10 from products
|
||||
} {660}
|
||||
|
||||
do_execsql_test multiply-int-agg {
|
||||
SELECT 10 * sum(id) from products
|
||||
} {660}
|
||||
|
||||
do_execsql_test multiply-agg-float {
|
||||
SELECT sum(id) * 10.1 from products
|
||||
} {666.6}
|
||||
|
||||
do_execsql_test multiply-float-agg {
|
||||
SELECT 10.1 * sum(id) from products
|
||||
} {666.6}
|
||||
|
||||
do_execsql_test multiply-agg-int-agg-int {
|
||||
SELECT sum(id) * sum(id) from products
|
||||
} {4356}
|
||||
|
||||
do_execsql_test multiply-agg-float-agg-float {
|
||||
SELECT sum(price) * sum(price) from products
|
||||
} {388129.0}
|
||||
|
||||
do_execsql_test multiply-agg-int-agg-float {
|
||||
SELECT sum(id) * sum(price) from products
|
||||
} {41118.0}
|
||||
|
||||
do_execsql_test multiply-agg-float-agg-int {
|
||||
SELECT sum(price) * sum(id) from products
|
||||
} {41118.0}
|
||||
|
||||
|
||||
|
||||
do_execsql_test divide-agg-int {
|
||||
SELECT sum(id) / 10 from products
|
||||
} {6}
|
||||
|
||||
do_execsql_test divide-int-agg {
|
||||
SELECT 660 / sum(id) from products
|
||||
} {10}
|
||||
|
||||
do_execsql_test divide-agg-float {
|
||||
SELECT sum(id) / 1.5 from products
|
||||
} {44.0}
|
||||
|
||||
do_execsql_test divide-float-agg {
|
||||
SELECT 66.0 / sum(id) from products
|
||||
} {1.0}
|
||||
|
||||
do_execsql_test divide-agg-int-agg-int {
|
||||
SELECT sum(id) / sum(id) from products
|
||||
} {1}
|
||||
|
||||
do_execsql_test divide-agg-float-agg-float {
|
||||
SELECT sum(price) / sum(price) from products
|
||||
} {1.0}
|
||||
|
||||
do_execsql_test divide-agg-int-agg-float {
|
||||
SELECT sum(id) / min(price) from products
|
||||
} {66.0}
|
||||
|
||||
do_execsql_test divide-agg-float-agg-int {
|
||||
SELECT min(price) / min(id) from products
|
||||
} {1.0}
|
||||
Reference in New Issue
Block a user