mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-05 09:14:24 +01:00
Merge 'Support math functions' from Lauri Virtanen
Add support for math scalar functions of SQLite: https://sqlite.org/lang_mathfunc.html Since SQLite CLI and Limbo CLI present floating point numbers with different precision, I added `do_execsql_test_tolerance` which tests that floating point results close enough of the expected value. However, we probably could make Limbo's floating point presentation match to SQLite. Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com> Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com> Closes #460
This commit is contained in:
35
COMPAT.md
35
COMPAT.md
@@ -160,6 +160,41 @@ Feature support of [sqlite expr syntax](https://www.sqlite.org/lang_expr.html).
|
||||
| upper(X) | Yes | |
|
||||
| zeroblob(N) | Yes | |
|
||||
|
||||
### Mathematical functions
|
||||
|
||||
| Function | Status | Comment |
|
||||
| ---------- | ------ | ------- |
|
||||
| acos(X) | Yes | |
|
||||
| acosh(X) | Yes | |
|
||||
| asin(X) | Yes | |
|
||||
| asinh(X) | Yes | |
|
||||
| atan(X) | Yes | |
|
||||
| atan2(Y,X) | Yes | |
|
||||
| atanh(X) | Yes | |
|
||||
| ceil(X) | Yes | |
|
||||
| ceiling(X) | Yes | |
|
||||
| cos(X) | Yes | |
|
||||
| cosh(X) | Yes | |
|
||||
| degrees(X) | Yes | |
|
||||
| exp(X) | Yes | |
|
||||
| floor(X) | Yes | |
|
||||
| ln(X) | Yes | |
|
||||
| log(B,X) | Yes | |
|
||||
| log(X) | Yes | |
|
||||
| log10(X) | Yes | |
|
||||
| log2(X) | Yes | |
|
||||
| mod(X,Y) | Yes | |
|
||||
| pi() | Yes | |
|
||||
| pow(X,Y) | Yes | |
|
||||
| power(X,Y) | Yes | |
|
||||
| radians(X) | Yes | |
|
||||
| sin(X) | Yes | |
|
||||
| sinh(X) | Yes | |
|
||||
| sqrt(X) | Yes | |
|
||||
| tan(X) | Yes | |
|
||||
| tanh(X) | Yes | |
|
||||
| trunc(X) | Yes | |
|
||||
|
||||
### Aggregate functions
|
||||
|
||||
| Function | Status | Comment |
|
||||
|
||||
146
core/function.rs
146
core/function.rs
@@ -139,10 +139,126 @@ impl Display for ScalarFunc {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum MathFunc {
|
||||
Acos,
|
||||
Acosh,
|
||||
Asin,
|
||||
Asinh,
|
||||
Atan,
|
||||
Atan2,
|
||||
Atanh,
|
||||
Ceil,
|
||||
Ceiling,
|
||||
Cos,
|
||||
Cosh,
|
||||
Degrees,
|
||||
Exp,
|
||||
Floor,
|
||||
Ln,
|
||||
Log,
|
||||
Log10,
|
||||
Log2,
|
||||
Mod,
|
||||
Pi,
|
||||
Pow,
|
||||
Power,
|
||||
Radians,
|
||||
Sin,
|
||||
Sinh,
|
||||
Sqrt,
|
||||
Tan,
|
||||
Tanh,
|
||||
Trunc,
|
||||
}
|
||||
|
||||
pub enum MathFuncArity {
|
||||
Nullary,
|
||||
Unary,
|
||||
Binary,
|
||||
UnaryOrBinary,
|
||||
}
|
||||
|
||||
impl MathFunc {
|
||||
pub fn arity(&self) -> MathFuncArity {
|
||||
match self {
|
||||
MathFunc::Pi => MathFuncArity::Nullary,
|
||||
|
||||
MathFunc::Acos
|
||||
| MathFunc::Acosh
|
||||
| MathFunc::Asin
|
||||
| MathFunc::Asinh
|
||||
| MathFunc::Atan
|
||||
| MathFunc::Atanh
|
||||
| MathFunc::Ceil
|
||||
| MathFunc::Ceiling
|
||||
| MathFunc::Cos
|
||||
| MathFunc::Cosh
|
||||
| MathFunc::Degrees
|
||||
| MathFunc::Exp
|
||||
| MathFunc::Floor
|
||||
| MathFunc::Ln
|
||||
| MathFunc::Log10
|
||||
| MathFunc::Log2
|
||||
| MathFunc::Radians
|
||||
| MathFunc::Sin
|
||||
| MathFunc::Sinh
|
||||
| MathFunc::Sqrt
|
||||
| MathFunc::Tan
|
||||
| MathFunc::Tanh
|
||||
| MathFunc::Trunc => MathFuncArity::Unary,
|
||||
|
||||
MathFunc::Atan2 | MathFunc::Mod | MathFunc::Pow | MathFunc::Power => {
|
||||
MathFuncArity::Binary
|
||||
}
|
||||
|
||||
MathFunc::Log => MathFuncArity::UnaryOrBinary,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for MathFunc {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let str = match self {
|
||||
MathFunc::Acos => "acos".to_string(),
|
||||
MathFunc::Acosh => "acosh".to_string(),
|
||||
MathFunc::Asin => "asin".to_string(),
|
||||
MathFunc::Asinh => "asinh".to_string(),
|
||||
MathFunc::Atan => "atan".to_string(),
|
||||
MathFunc::Atan2 => "atan2".to_string(),
|
||||
MathFunc::Atanh => "atanh".to_string(),
|
||||
MathFunc::Ceil => "ceil".to_string(),
|
||||
MathFunc::Ceiling => "ceiling".to_string(),
|
||||
MathFunc::Cos => "cos".to_string(),
|
||||
MathFunc::Cosh => "cosh".to_string(),
|
||||
MathFunc::Degrees => "degrees".to_string(),
|
||||
MathFunc::Exp => "exp".to_string(),
|
||||
MathFunc::Floor => "floor".to_string(),
|
||||
MathFunc::Ln => "ln".to_string(),
|
||||
MathFunc::Log => "log".to_string(),
|
||||
MathFunc::Log10 => "log10".to_string(),
|
||||
MathFunc::Log2 => "log2".to_string(),
|
||||
MathFunc::Mod => "mod".to_string(),
|
||||
MathFunc::Pi => "pi".to_string(),
|
||||
MathFunc::Pow => "pow".to_string(),
|
||||
MathFunc::Power => "power".to_string(),
|
||||
MathFunc::Radians => "radians".to_string(),
|
||||
MathFunc::Sin => "sin".to_string(),
|
||||
MathFunc::Sinh => "sinh".to_string(),
|
||||
MathFunc::Sqrt => "sqrt".to_string(),
|
||||
MathFunc::Tan => "tan".to_string(),
|
||||
MathFunc::Tanh => "tanh".to_string(),
|
||||
MathFunc::Trunc => "trunc".to_string(),
|
||||
};
|
||||
write!(f, "{}", str)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Func {
|
||||
Agg(AggFunc),
|
||||
Scalar(ScalarFunc),
|
||||
Math(MathFunc),
|
||||
#[cfg(feature = "json")]
|
||||
Json(JsonFunc),
|
||||
}
|
||||
@@ -152,6 +268,7 @@ impl Display for Func {
|
||||
match self {
|
||||
Func::Agg(agg_func) => write!(f, "{}", agg_func.to_string()),
|
||||
Func::Scalar(scalar_func) => write!(f, "{}", scalar_func),
|
||||
Func::Math(math_func) => write!(f, "{}", math_func),
|
||||
#[cfg(feature = "json")]
|
||||
Func::Json(json_func) => write!(f, "{}", json_func),
|
||||
}
|
||||
@@ -216,6 +333,35 @@ impl Func {
|
||||
"unhex" => Ok(Func::Scalar(ScalarFunc::Unhex)),
|
||||
"zeroblob" => Ok(Func::Scalar(ScalarFunc::ZeroBlob)),
|
||||
"soundex" => Ok(Func::Scalar(ScalarFunc::Soundex)),
|
||||
"acos" => Ok(Func::Math(MathFunc::Acos)),
|
||||
"acosh" => Ok(Func::Math(MathFunc::Acosh)),
|
||||
"asin" => Ok(Func::Math(MathFunc::Asin)),
|
||||
"asinh" => Ok(Func::Math(MathFunc::Asinh)),
|
||||
"atan" => Ok(Func::Math(MathFunc::Atan)),
|
||||
"atan2" => Ok(Func::Math(MathFunc::Atan2)),
|
||||
"atanh" => Ok(Func::Math(MathFunc::Atanh)),
|
||||
"ceil" => Ok(Func::Math(MathFunc::Ceil)),
|
||||
"ceiling" => Ok(Func::Math(MathFunc::Ceiling)),
|
||||
"cos" => Ok(Func::Math(MathFunc::Cos)),
|
||||
"cosh" => Ok(Func::Math(MathFunc::Cosh)),
|
||||
"degrees" => Ok(Func::Math(MathFunc::Degrees)),
|
||||
"exp" => Ok(Func::Math(MathFunc::Exp)),
|
||||
"floor" => Ok(Func::Math(MathFunc::Floor)),
|
||||
"ln" => Ok(Func::Math(MathFunc::Ln)),
|
||||
"log" => Ok(Func::Math(MathFunc::Log)),
|
||||
"log10" => Ok(Func::Math(MathFunc::Log10)),
|
||||
"log2" => Ok(Func::Math(MathFunc::Log2)),
|
||||
"mod" => Ok(Func::Math(MathFunc::Mod)),
|
||||
"pi" => Ok(Func::Math(MathFunc::Pi)),
|
||||
"pow" => Ok(Func::Math(MathFunc::Pow)),
|
||||
"power" => Ok(Func::Math(MathFunc::Power)),
|
||||
"radians" => Ok(Func::Math(MathFunc::Radians)),
|
||||
"sin" => Ok(Func::Math(MathFunc::Sin)),
|
||||
"sinh" => Ok(Func::Math(MathFunc::Sinh)),
|
||||
"sqrt" => Ok(Func::Math(MathFunc::Sqrt)),
|
||||
"tan" => Ok(Func::Math(MathFunc::Tan)),
|
||||
"tanh" => Ok(Func::Math(MathFunc::Tanh)),
|
||||
"trunc" => Ok(Func::Math(MathFunc::Trunc)),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use sqlite3_parser::ast::{self, UnaryOperator};
|
||||
|
||||
#[cfg(feature = "json")]
|
||||
use crate::function::JsonFunc;
|
||||
use crate::function::{AggFunc, Func, FuncCtx, ScalarFunc};
|
||||
use crate::function::{AggFunc, Func, FuncCtx, MathFuncArity, ScalarFunc};
|
||||
use crate::schema::Type;
|
||||
use crate::util::normalize_ident;
|
||||
use crate::vdbe::{builder::ProgramBuilder, BranchOffset, Insn};
|
||||
@@ -1603,6 +1603,134 @@ pub fn translate_expr(
|
||||
}
|
||||
}
|
||||
}
|
||||
Func::Math(math_func) => match math_func.arity() {
|
||||
MathFuncArity::Nullary => {
|
||||
if args.is_some() {
|
||||
crate::bail_parse_error!("{} function with arguments", math_func);
|
||||
}
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: 0,
|
||||
dest: target_register,
|
||||
func: func_ctx,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
|
||||
MathFuncArity::Unary => {
|
||||
let args = if let Some(args) = args {
|
||||
if args.len() != 1 {
|
||||
crate::bail_parse_error!(
|
||||
"{} function with not exactly 1 argument",
|
||||
math_func
|
||||
);
|
||||
}
|
||||
args
|
||||
} else {
|
||||
crate::bail_parse_error!("{} function with no arguments", math_func);
|
||||
};
|
||||
|
||||
let reg = program.alloc_register();
|
||||
|
||||
translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
&args[0],
|
||||
reg,
|
||||
precomputed_exprs_to_registers,
|
||||
)?;
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: reg,
|
||||
dest: target_register,
|
||||
func: func_ctx,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
|
||||
MathFuncArity::Binary => {
|
||||
let args = if let Some(args) = args {
|
||||
if args.len() != 2 {
|
||||
crate::bail_parse_error!(
|
||||
"{} function with not exactly 2 arguments",
|
||||
math_func
|
||||
);
|
||||
}
|
||||
args
|
||||
} else {
|
||||
crate::bail_parse_error!("{} function with no arguments", math_func);
|
||||
};
|
||||
|
||||
let reg1 = program.alloc_register();
|
||||
let reg2 = program.alloc_register();
|
||||
|
||||
translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
&args[0],
|
||||
reg1,
|
||||
precomputed_exprs_to_registers,
|
||||
)?;
|
||||
if let ast::Expr::Literal(_) = &args[0] {
|
||||
program.mark_last_insn_constant();
|
||||
}
|
||||
|
||||
translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
&args[1],
|
||||
reg2,
|
||||
precomputed_exprs_to_registers,
|
||||
)?;
|
||||
if let ast::Expr::Literal(_) = &args[1] {
|
||||
program.mark_last_insn_constant();
|
||||
}
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: target_register + 1,
|
||||
dest: target_register,
|
||||
func: func_ctx,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
|
||||
MathFuncArity::UnaryOrBinary => {
|
||||
let args = if let Some(args) = args {
|
||||
if args.len() > 2 {
|
||||
crate::bail_parse_error!(
|
||||
"{} function with more than 2 arguments",
|
||||
math_func
|
||||
);
|
||||
}
|
||||
args
|
||||
} else {
|
||||
crate::bail_parse_error!("{} function with no arguments", math_func);
|
||||
};
|
||||
|
||||
let regs = program.alloc_registers(args.len());
|
||||
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
arg,
|
||||
regs + i,
|
||||
precomputed_exprs_to_registers,
|
||||
)?;
|
||||
}
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: regs,
|
||||
dest: target_register,
|
||||
func: func_ctx,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
ast::Expr::FunctionCallStar { .. } => todo!(),
|
||||
|
||||
157
core/vdbe/mod.rs
157
core/vdbe/mod.rs
@@ -24,7 +24,7 @@ pub mod sorter;
|
||||
mod datetime;
|
||||
|
||||
use crate::error::{LimboError, SQLITE_CONSTRAINT_PRIMARYKEY};
|
||||
use crate::function::{AggFunc, FuncCtx, ScalarFunc};
|
||||
use crate::function::{AggFunc, FuncCtx, MathFunc, MathFuncArity, ScalarFunc};
|
||||
use crate::pseudo::PseudoCursor;
|
||||
use crate::schema::Table;
|
||||
use crate::storage::sqlite3_ondisk::DatabaseHeader;
|
||||
@@ -2491,6 +2491,58 @@ impl Program {
|
||||
state.registers[*dest] = exec_replace(source, pattern, replacement);
|
||||
}
|
||||
},
|
||||
crate::function::Func::Math(math_func) => match math_func.arity() {
|
||||
MathFuncArity::Nullary => match math_func {
|
||||
MathFunc::Pi => {
|
||||
state.registers[*dest] =
|
||||
OwnedValue::Float(std::f64::consts::PI);
|
||||
}
|
||||
_ => {
|
||||
unreachable!(
|
||||
"Unexpected mathematical Nullary function {:?}",
|
||||
math_func
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
MathFuncArity::Unary => {
|
||||
let reg_value = &state.registers[*start_reg];
|
||||
let result = exec_math_unary(reg_value, math_func);
|
||||
state.registers[*dest] = result;
|
||||
}
|
||||
|
||||
MathFuncArity::Binary => {
|
||||
let lhs = &state.registers[*start_reg];
|
||||
let rhs = &state.registers[*start_reg + 1];
|
||||
let result = exec_math_binary(lhs, rhs, math_func);
|
||||
state.registers[*dest] = result;
|
||||
}
|
||||
|
||||
MathFuncArity::UnaryOrBinary => match math_func {
|
||||
MathFunc::Log => {
|
||||
let result = match arg_count {
|
||||
1 => {
|
||||
let arg = &state.registers[*start_reg];
|
||||
exec_math_log(arg, None)
|
||||
}
|
||||
2 => {
|
||||
let base = &state.registers[*start_reg];
|
||||
let arg = &state.registers[*start_reg + 1];
|
||||
exec_math_log(arg, Some(base))
|
||||
}
|
||||
_ => unreachable!(
|
||||
"{:?} function with unexpected number of arguments",
|
||||
math_func
|
||||
),
|
||||
};
|
||||
state.registers[*dest] = result;
|
||||
}
|
||||
_ => unreachable!(
|
||||
"Unexpected mathematical UnaryOrBinary function {:?}",
|
||||
math_func
|
||||
),
|
||||
},
|
||||
},
|
||||
crate::function::Func::Agg(_) => {
|
||||
unreachable!("Aggregate functions should not be handled here")
|
||||
}
|
||||
@@ -3597,6 +3649,109 @@ fn execute_sqlite_version(version_integer: i64) -> String {
|
||||
format!("{}.{}.{}", major, minor, release)
|
||||
}
|
||||
|
||||
fn to_f64(reg: &OwnedValue) -> Option<f64> {
|
||||
match reg {
|
||||
OwnedValue::Integer(i) => Some(*i as f64),
|
||||
OwnedValue::Float(f) => Some(*f),
|
||||
OwnedValue::Text(t) => t.parse::<f64>().ok(),
|
||||
OwnedValue::Agg(ctx) => to_f64(ctx.final_value()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_math_unary(reg: &OwnedValue, function: &MathFunc) -> OwnedValue {
|
||||
// In case of some functions and integer input, return the input as is
|
||||
if let OwnedValue::Integer(_) = reg {
|
||||
if matches! { function, MathFunc::Ceil | MathFunc::Ceiling | MathFunc::Floor | MathFunc::Trunc }
|
||||
{
|
||||
return reg.clone();
|
||||
}
|
||||
}
|
||||
|
||||
let f = match to_f64(reg) {
|
||||
Some(f) => f,
|
||||
None => return OwnedValue::Null,
|
||||
};
|
||||
|
||||
let result = match function {
|
||||
MathFunc::Acos => f.acos(),
|
||||
MathFunc::Acosh => f.acosh(),
|
||||
MathFunc::Asin => f.asin(),
|
||||
MathFunc::Asinh => f.asinh(),
|
||||
MathFunc::Atan => f.atan(),
|
||||
MathFunc::Atanh => f.atanh(),
|
||||
MathFunc::Ceil | MathFunc::Ceiling => f.ceil(),
|
||||
MathFunc::Cos => f.cos(),
|
||||
MathFunc::Cosh => f.cosh(),
|
||||
MathFunc::Degrees => f.to_degrees(),
|
||||
MathFunc::Exp => f.exp(),
|
||||
MathFunc::Floor => f.floor(),
|
||||
MathFunc::Ln => f.ln(),
|
||||
MathFunc::Log10 => f.log10(),
|
||||
MathFunc::Log2 => f.log2(),
|
||||
MathFunc::Radians => f.to_radians(),
|
||||
MathFunc::Sin => f.sin(),
|
||||
MathFunc::Sinh => f.sinh(),
|
||||
MathFunc::Sqrt => f.sqrt(),
|
||||
MathFunc::Tan => f.tan(),
|
||||
MathFunc::Tanh => f.tanh(),
|
||||
MathFunc::Trunc => f.trunc(),
|
||||
_ => unreachable!("Unexpected mathematical unary function {:?}", function),
|
||||
};
|
||||
|
||||
if result.is_nan() {
|
||||
OwnedValue::Null
|
||||
} else {
|
||||
OwnedValue::Float(result)
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_math_binary(lhs: &OwnedValue, rhs: &OwnedValue, function: &MathFunc) -> OwnedValue {
|
||||
let lhs = match to_f64(lhs) {
|
||||
Some(f) => f,
|
||||
None => return OwnedValue::Null,
|
||||
};
|
||||
|
||||
let rhs = match to_f64(rhs) {
|
||||
Some(f) => f,
|
||||
None => return OwnedValue::Null,
|
||||
};
|
||||
|
||||
let result = match function {
|
||||
MathFunc::Atan2 => lhs.atan2(rhs),
|
||||
MathFunc::Mod => lhs % rhs,
|
||||
MathFunc::Pow | MathFunc::Power => lhs.powf(rhs),
|
||||
_ => unreachable!("Unexpected mathematical binary function {:?}", function),
|
||||
};
|
||||
|
||||
if result.is_nan() {
|
||||
OwnedValue::Null
|
||||
} else {
|
||||
OwnedValue::Float(result)
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_math_log(arg: &OwnedValue, base: Option<&OwnedValue>) -> OwnedValue {
|
||||
let f = match to_f64(arg) {
|
||||
Some(f) => f,
|
||||
None => return OwnedValue::Null,
|
||||
};
|
||||
|
||||
let base = match base {
|
||||
Some(base) => match to_f64(base) {
|
||||
Some(f) => f,
|
||||
None => return OwnedValue::Null,
|
||||
},
|
||||
None => 10.0,
|
||||
};
|
||||
|
||||
if f <= 0.0 || base <= 0.0 || base == 1.0 {
|
||||
return OwnedValue::Null;
|
||||
}
|
||||
|
||||
OwnedValue::Float(f.log(base))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
set testdir [file dirname $argv0]
|
||||
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
|
||||
|
||||
do_execsql_test add-int {
|
||||
SELECT 10 + 1
|
||||
} {11}
|
||||
@@ -361,3 +365,663 @@ do_execsql_test bitwise-not-zero {
|
||||
SELECT ~0
|
||||
} {-1}
|
||||
|
||||
|
||||
do_execsql_test_tolerance pi {
|
||||
SELECT pi()
|
||||
} {3.14159265358979} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance acos-int {
|
||||
SELECT acos(1)
|
||||
} {0.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance acos-float {
|
||||
SELECT acos(-0.5)
|
||||
} {2.0943951023931957} $tolerance
|
||||
|
||||
do_execsql_test_tolerance acos-str {
|
||||
SELECT acos('-0.5')
|
||||
} {2.0943951023931957} $tolerance
|
||||
|
||||
do_execsql_test_tolerance acos-null {
|
||||
SELECT acos(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance acosh-int {
|
||||
SELECT acosh(1)
|
||||
} {0.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance acosh-float {
|
||||
SELECT acosh(1.5)
|
||||
} {0.962423650119207} $tolerance
|
||||
|
||||
do_execsql_test_tolerance acosh-str {
|
||||
SELECT acosh('1.5')
|
||||
} {0.962423650119207} $tolerance
|
||||
|
||||
do_execsql_test_tolerance acosh-invalid {
|
||||
SELECT acosh(0.99)
|
||||
} {} $tolerance
|
||||
|
||||
do_execsql_test_tolerance acosh-null {
|
||||
SELECT acosh(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance asin-int {
|
||||
SELECT asin(1)
|
||||
} {1.5707963267948966} $tolerance
|
||||
|
||||
do_execsql_test_tolerance asin-float {
|
||||
SELECT asin(-0.5)
|
||||
} {-0.5235987755982989} $tolerance
|
||||
|
||||
do_execsql_test_tolerance asin-str {
|
||||
SELECT asin('-0.5')
|
||||
} {-0.5235987755982989} $tolerance
|
||||
|
||||
do_execsql_test_tolerance asin-null {
|
||||
SELECT asin(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance sin-int {
|
||||
SELECT sin(1)
|
||||
} {0.841470984807897} $tolerance
|
||||
|
||||
do_execsql_test_tolerance sin-float {
|
||||
SELECT sin(-0.5)
|
||||
} {-0.479425538604203} $tolerance
|
||||
|
||||
do_execsql_test_tolerance sin-str {
|
||||
SELECT sin('-0.5')
|
||||
} {-0.479425538604203} $tolerance
|
||||
|
||||
do_execsql_test_tolerance sin-null {
|
||||
SELECT sin(null)
|
||||
} {} $tolerance
|
||||
|
||||
do_execsql_test_tolerance sin-products-id {
|
||||
SELECT sin(id) from products limit 5
|
||||
} {0.8414709848078965
|
||||
0.9092974268256817
|
||||
0.1411200080598672
|
||||
-0.7568024953079282
|
||||
-0.9589242746631385} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance asinh-int {
|
||||
SELECT asinh(1)
|
||||
} {0.881373587019543} $tolerance
|
||||
|
||||
do_execsql_test_tolerance asinh-float {
|
||||
SELECT asinh(-0.5)
|
||||
} {-0.48121182505960347} $tolerance
|
||||
|
||||
do_execsql_test_tolerance asinh-str {
|
||||
SELECT asinh('-0.5')
|
||||
} {-0.48121182505960347} $tolerance
|
||||
|
||||
do_execsql_test_tolerance asinh-null {
|
||||
SELECT asinh(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance atan-int {
|
||||
SELECT atan(1)
|
||||
} {0.7853981633974483} $tolerance
|
||||
|
||||
do_execsql_test_tolerance atan-float {
|
||||
SELECT atan(-0.5)
|
||||
} {-0.4636476090008061} $tolerance
|
||||
|
||||
do_execsql_test_tolerance atan-str {
|
||||
SELECT atan('-0.5')
|
||||
} {-0.4636476090008061} $tolerance
|
||||
|
||||
do_execsql_test_tolerance atan-null {
|
||||
SELECT atan(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance tan-int {
|
||||
SELECT tan(1)
|
||||
} {1.5574077246549} $tolerance
|
||||
|
||||
do_execsql_test_tolerance tan-float {
|
||||
SELECT tan(-0.5)
|
||||
} {-0.54630248984379} $tolerance
|
||||
|
||||
do_execsql_test_tolerance tan-str {
|
||||
SELECT tan('-0.5')
|
||||
} {-0.54630248984379} $tolerance
|
||||
|
||||
do_execsql_test_tolerance tan-null {
|
||||
SELECT tan(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance atanh-int {
|
||||
SELECT atanh(0)
|
||||
} {0.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance atanh-float {
|
||||
SELECT atanh(-0.5)
|
||||
} {-0.5493061443340548} $tolerance
|
||||
|
||||
do_execsql_test_tolerance atanh-str {
|
||||
SELECT atanh('-0.5')
|
||||
} {-0.5493061443340548} $tolerance
|
||||
|
||||
do_execsql_test_tolerance atanh-null {
|
||||
SELECT atanh(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test ceil-int {
|
||||
SELECT ceil(1)
|
||||
} {1}
|
||||
|
||||
do_execsql_test ceil-float {
|
||||
SELECT ceil(-1.5)
|
||||
} {-1.0}
|
||||
|
||||
do_execsql_test ceil-str {
|
||||
SELECT ceil('1.5')
|
||||
} {2.0}
|
||||
|
||||
do_execsql_test ceil-null {
|
||||
SELECT ceil(null)
|
||||
} {}
|
||||
|
||||
|
||||
do_execsql_test ceiling-int {
|
||||
SELECT ceiling(1)
|
||||
} {1}
|
||||
|
||||
do_execsql_test ceiling-float {
|
||||
SELECT ceiling(-1.5)
|
||||
} {-1.0}
|
||||
|
||||
do_execsql_test ceiling-str {
|
||||
SELECT ceiling('1.5')
|
||||
} {2.0}
|
||||
|
||||
do_execsql_test ceiling-null {
|
||||
SELECT ceiling(null)
|
||||
} {}
|
||||
|
||||
|
||||
do_execsql_test_tolerance cos-int {
|
||||
SELECT cos(1)
|
||||
} {0.54030230586814} $tolerance
|
||||
|
||||
do_execsql_test_tolerance cos-float {
|
||||
SELECT cos(-0.5)
|
||||
} {0.877582561890373} $tolerance
|
||||
|
||||
do_execsql_test_tolerance cos-str {
|
||||
SELECT cos('-0.5')
|
||||
} {0.877582561890373} $tolerance
|
||||
|
||||
do_execsql_test_tolerance cos-null {
|
||||
SELECT cos(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance cosh-int {
|
||||
SELECT cosh(1)
|
||||
} {1.54308063481524} $tolerance
|
||||
|
||||
do_execsql_test_tolerance cosh-float {
|
||||
SELECT cosh(-0.5)
|
||||
} {1.12762596520638} $tolerance
|
||||
|
||||
do_execsql_test_tolerance cosh-str {
|
||||
SELECT cosh('-0.5')
|
||||
} {1.12762596520638} $tolerance
|
||||
|
||||
do_execsql_test_tolerance cosh-null {
|
||||
SELECT cosh(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance degrees-int {
|
||||
SELECT degrees(1)
|
||||
} {57.2957795130823} $tolerance
|
||||
|
||||
do_execsql_test_tolerance degrees-float {
|
||||
SELECT degrees(-0.5)
|
||||
} {-28.6478897565412} $tolerance
|
||||
|
||||
do_execsql_test_tolerance degrees-str {
|
||||
SELECT degrees('-0.5')
|
||||
} {-28.6478897565412} $tolerance
|
||||
|
||||
do_execsql_test_tolerance degrees-null {
|
||||
SELECT degrees(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance exp-int {
|
||||
SELECT exp(1)
|
||||
} {2.71828182845905} $tolerance
|
||||
|
||||
do_execsql_test_tolerance exp-float {
|
||||
SELECT exp(-0.5)
|
||||
} {0.606530659712633} $tolerance
|
||||
|
||||
do_execsql_test_tolerance exp-str {
|
||||
SELECT exp('-0.5')
|
||||
} {0.606530659712633} $tolerance
|
||||
|
||||
do_execsql_test_tolerance exp-null {
|
||||
SELECT exp(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test floor-int {
|
||||
SELECT floor(1)
|
||||
} {1}
|
||||
|
||||
do_execsql_test floor-float {
|
||||
SELECT floor(-1.5)
|
||||
} {-2.0}
|
||||
|
||||
do_execsql_test floor-str {
|
||||
SELECT floor('1.5')
|
||||
} {1.0}
|
||||
|
||||
do_execsql_test floor-null {
|
||||
SELECT floor(null)
|
||||
} {}
|
||||
|
||||
|
||||
do_execsql_test_tolerance ln-int {
|
||||
SELECT ln(1)
|
||||
} {0.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance ln-float {
|
||||
SELECT ln(0.5)
|
||||
} {-0.693147180559945} $tolerance
|
||||
|
||||
do_execsql_test_tolerance ln-str {
|
||||
SELECT ln('0.5')
|
||||
} {-0.693147180559945} $tolerance
|
||||
|
||||
do_execsql_test_tolerance ln-negative {
|
||||
SELECT ln(-0.5)
|
||||
} {} $tolerance
|
||||
|
||||
do_execsql_test_tolerance ln-null {
|
||||
SELECT ln(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance log10-int {
|
||||
SELECT log10(1)
|
||||
} {0.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log10-float {
|
||||
SELECT log10(0.5)
|
||||
} {-0.301029995663981} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log10-str {
|
||||
SELECT log10('0.5')
|
||||
} {-0.301029995663981} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log10-negative {
|
||||
SELECT log10(-0.5)
|
||||
} {} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log10-null {
|
||||
SELECT log10(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance log2-int {
|
||||
SELECT log2(1)
|
||||
} {0.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log2-float {
|
||||
SELECT log2(0.5)
|
||||
} {-1.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log2-str {
|
||||
SELECT log2('0.5')
|
||||
} {-1.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log2-negative {
|
||||
SELECT log2(-0.5)
|
||||
} {} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log2-null {
|
||||
SELECT log2(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance radians-int {
|
||||
SELECT radians(1)
|
||||
} {0.0174532925199433} $tolerance
|
||||
|
||||
do_execsql_test_tolerance radians-float {
|
||||
SELECT radians(-0.5)
|
||||
} {-0.00872664625997165} $tolerance
|
||||
|
||||
do_execsql_test_tolerance radians-str {
|
||||
SELECT radians('-0.5')
|
||||
} {-0.00872664625997165} $tolerance
|
||||
|
||||
do_execsql_test_tolerance radians-null {
|
||||
SELECT radians(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance sinh-int {
|
||||
SELECT sinh(1)
|
||||
} {1.1752011936438} $tolerance
|
||||
|
||||
do_execsql_test_tolerance sinh-float {
|
||||
SELECT sinh(-0.5)
|
||||
} {-0.521095305493747} $tolerance
|
||||
|
||||
do_execsql_test_tolerance sinh-str {
|
||||
SELECT sinh('-0.5')
|
||||
} {-0.521095305493747} $tolerance
|
||||
|
||||
do_execsql_test_tolerance sinh-null {
|
||||
SELECT sinh(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance sqrt-int {
|
||||
SELECT sqrt(1)
|
||||
} {1.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance sqrt-float {
|
||||
SELECT sqrt(0.5)
|
||||
} {0.707106781186548} $tolerance
|
||||
|
||||
do_execsql_test_tolerance sqrt-str {
|
||||
SELECT sqrt('0.5')
|
||||
} {0.707106781186548} $tolerance
|
||||
|
||||
do_execsql_test_tolerance sqrt-negative {
|
||||
SELECT sqrt(-0.5)
|
||||
} {} $tolerance
|
||||
|
||||
do_execsql_test_tolerance sqrt-null {
|
||||
SELECT sqrt(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test_tolerance tanh-int {
|
||||
SELECT tanh(1)
|
||||
} {0.761594155955765} $tolerance
|
||||
|
||||
do_execsql_test_tolerance tanh-float {
|
||||
SELECT tanh(-0.5)
|
||||
} {-0.46211715726001} $tolerance
|
||||
|
||||
do_execsql_test_tolerance tanh-str {
|
||||
SELECT tanh('-0.5')
|
||||
} {-0.46211715726001} $tolerance
|
||||
|
||||
do_execsql_test_tolerance tanh-null {
|
||||
SELECT tanh(null)
|
||||
} {} $tolerance
|
||||
|
||||
|
||||
do_execsql_test trunc-int {
|
||||
SELECT trunc(1)
|
||||
} {1}
|
||||
|
||||
do_execsql_test trunc-float {
|
||||
SELECT trunc(2.5)
|
||||
} {2.0}
|
||||
|
||||
do_execsql_test trunc-float-negative {
|
||||
SELECT trunc(-2.5)
|
||||
} {-2.0}
|
||||
|
||||
do_execsql_test trunc-str {
|
||||
SELECT trunc('2.5')
|
||||
} {2.0}
|
||||
|
||||
do_execsql_test trunc-null {
|
||||
SELECT trunc(null)
|
||||
} {}
|
||||
|
||||
|
||||
do_execsql_test_tolerance atan2-int-int {
|
||||
SELECT atan2(5, -1)
|
||||
} {1.76819188664478} $tolerance
|
||||
|
||||
do_execsql_test_tolerance atan2-int-float {
|
||||
SELECT atan2(5, -1.5)
|
||||
} {1.86225312127276} $tolerance
|
||||
|
||||
do_execsql_test_tolerance atan2-int-str {
|
||||
SELECT atan2(5, '-1.5')
|
||||
} {1.86225312127276} $tolerance
|
||||
|
||||
do_execsql_test_tolerance atan2-float-int {
|
||||
SELECT atan2(5.5, 10)
|
||||
} {0.502843210927861} $tolerance
|
||||
|
||||
do_execsql_test_tolerance atan2-float-float {
|
||||
SELECT atan2(5.5, -1.5)
|
||||
} {1.83704837594582} $tolerance
|
||||
|
||||
do_execsql_test_tolerance atan2-float-str {
|
||||
SELECT atan2(5.5, '-1.5')
|
||||
} {1.83704837594582} $tolerance
|
||||
|
||||
do_execsql_test_tolerance atan2-str-str {
|
||||
SELECT atan2('5.5', '-1.5')
|
||||
} {1.83704837594582} $tolerance
|
||||
|
||||
do_execsql_test atan2-null-int {
|
||||
SELECT atan2(null, 5)
|
||||
} {}
|
||||
|
||||
do_execsql_test atan2-int-null {
|
||||
SELECT atan2(5, null)
|
||||
} {}
|
||||
|
||||
|
||||
do_execsql_test_tolerance mod-int-int {
|
||||
SELECT mod(10, -3)
|
||||
} {1.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance mod-int-float {
|
||||
SELECT mod(5, -1.5)
|
||||
} {0.5} $tolerance
|
||||
|
||||
do_execsql_test_tolerance mod-int-str {
|
||||
SELECT mod(5, '-1.5')
|
||||
} {0.5} $tolerance
|
||||
|
||||
do_execsql_test_tolerance mod-float-int {
|
||||
SELECT mod(5.5, 2)
|
||||
} {1.5} $tolerance
|
||||
|
||||
do_execsql_test_tolerance mod-float-float {
|
||||
SELECT mod(5.5, -1.5)
|
||||
} {1.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance mod-float-str {
|
||||
SELECT mod(5.5, '-1.5')
|
||||
} {1.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance mod-str-str {
|
||||
SELECT mod('5.5', '-1.5')
|
||||
} {1.0} $tolerance
|
||||
|
||||
do_execsql_test mod-null-int {
|
||||
SELECT mod(null, 5)
|
||||
} {}
|
||||
|
||||
do_execsql_test mod-int-null {
|
||||
SELECT mod(5, null)
|
||||
} {}
|
||||
|
||||
do_execsql_test mod-float-zero {
|
||||
SELECT mod(1.5, 0)
|
||||
} {}
|
||||
|
||||
do_execsql_test mod-products-id {
|
||||
SELECT mod(products.id, 3) from products limit 5
|
||||
} {1.0
|
||||
2.0
|
||||
0.0
|
||||
1.0
|
||||
2.0}
|
||||
|
||||
do_execsql_test mod-products-price-id {
|
||||
SELECT mod(products.price, products.id) from products limit 5
|
||||
} {0.0
|
||||
0.0
|
||||
0.0
|
||||
1.0
|
||||
4.0}
|
||||
|
||||
|
||||
do_execsql_test_tolerance pow-int-int {
|
||||
SELECT pow(5, -1)
|
||||
} {0.2} $tolerance
|
||||
|
||||
do_execsql_test_tolerance pow-int-float {
|
||||
SELECT pow(5, -1.5)
|
||||
} {0.0894427190999916} $tolerance
|
||||
|
||||
do_execsql_test_tolerance pow-int-str {
|
||||
SELECT pow(5, '-1.5')
|
||||
} {0.0894427190999916} $tolerance
|
||||
|
||||
do_execsql_test_tolerance pow-float-int {
|
||||
SELECT pow(5.5, 2)
|
||||
} {30.25} $tolerance
|
||||
|
||||
do_execsql_test_tolerance pow-float-float {
|
||||
SELECT pow(5.5, -1.5)
|
||||
} {0.077527533220222} $tolerance
|
||||
|
||||
do_execsql_test_tolerance pow-float-str {
|
||||
SELECT pow(5.5, '-1.5')
|
||||
} {0.077527533220222} $tolerance
|
||||
|
||||
do_execsql_test_tolerance pow-str-str {
|
||||
SELECT pow('5.5', '-1.5')
|
||||
} {0.077527533220222} $tolerance
|
||||
|
||||
do_execsql_test pow-null-int {
|
||||
SELECT pow(null, 5)
|
||||
} {}
|
||||
|
||||
do_execsql_test pow-int-null {
|
||||
SELECT pow(5, null)
|
||||
} {}
|
||||
|
||||
|
||||
do_execsql_test_tolerance power-int-int {
|
||||
SELECT power(5, -1)
|
||||
} {0.2} $tolerance
|
||||
|
||||
do_execsql_test_tolerance power-int-float {
|
||||
SELECT power(5, -1.5)
|
||||
} {0.0894427190999916} $tolerance
|
||||
|
||||
do_execsql_test_tolerance power-int-str {
|
||||
SELECT power(5, '-1.5')
|
||||
} {0.0894427190999916} $tolerance
|
||||
|
||||
do_execsql_test_tolerance power-float-int {
|
||||
SELECT power(5.5, 2)
|
||||
} {30.25} $tolerance
|
||||
|
||||
do_execsql_test_tolerance power-float-float {
|
||||
SELECT power(5.5, -1.5)
|
||||
} {0.077527533220222} $tolerance
|
||||
|
||||
do_execsql_test_tolerance power-float-str {
|
||||
SELECT power(5.5, '-1.5')
|
||||
} {0.077527533220222} $tolerance
|
||||
|
||||
do_execsql_test_tolerance power-str-str {
|
||||
SELECT power('5.5', '-1.5')
|
||||
} {0.077527533220222} $tolerance
|
||||
|
||||
do_execsql_test power-null-int {
|
||||
SELECT power(null, 5)
|
||||
} {}
|
||||
|
||||
do_execsql_test power-int-null {
|
||||
SELECT power(5, null)
|
||||
} {}
|
||||
|
||||
|
||||
do_execsql_test_tolerance log-int {
|
||||
SELECT log(1)
|
||||
} {0.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log-float {
|
||||
SELECT log(1.5)
|
||||
} {0.176091259055681} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log-str {
|
||||
SELECT log('1.5')
|
||||
} {0.176091259055681} $tolerance
|
||||
|
||||
do_execsql_test log-negative {
|
||||
SELECT log(-1.5)
|
||||
} {}
|
||||
|
||||
do_execsql_test log-null {
|
||||
SELECT log(null)
|
||||
} {}
|
||||
|
||||
do_execsql_test_tolerance log-int-int {
|
||||
SELECT log(5, 1)
|
||||
} {0.0} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log-int-float {
|
||||
SELECT log(5, 1.5)
|
||||
} {0.251929636412592} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log-int-str {
|
||||
SELECT log(5, '1.5')
|
||||
} {0.251929636412592} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log-float-int {
|
||||
SELECT log(5.5, 10)
|
||||
} {1.35068935021985} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log-float-float {
|
||||
SELECT log(5.5, 1.5)
|
||||
} {0.237844588273313} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log-float-str {
|
||||
SELECT log(5.5, '1.5')
|
||||
} {0.237844588273313} $tolerance
|
||||
|
||||
do_execsql_test_tolerance log-str-str {
|
||||
SELECT log('5.5', '1.5')
|
||||
} {0.237844588273313} $tolerance
|
||||
|
||||
do_execsql_test log-negative-negative {
|
||||
SELECT log(-1.5, -1.5)
|
||||
} {}
|
||||
|
||||
do_execsql_test log-float-negative {
|
||||
SELECT log(1.5, -1.5)
|
||||
} {}
|
||||
|
||||
do_execsql_test log-null-int {
|
||||
SELECT log(null, 5)
|
||||
} {}
|
||||
|
||||
do_execsql_test log-int-null {
|
||||
SELECT log(5, null)
|
||||
} {}
|
||||
|
||||
@@ -32,3 +32,40 @@ proc do_execsql_test_on_specific_db {db_name test_name sql_statements expected_o
|
||||
set combined_expected_output [join $expected_outputs "\n"]
|
||||
run_test $::sqlite_exec $db_name $combined_sql $combined_expected_output
|
||||
}
|
||||
|
||||
proc within_tolerance {actual expected tolerance} {
|
||||
expr {abs($actual - $expected) <= $tolerance}
|
||||
}
|
||||
|
||||
# This function is used to test floating point values within a tolerance
|
||||
# FIXME: When Limbo's floating point presentation matches to SQLite, this could/should be removed
|
||||
proc do_execsql_test_tolerance {test_name sql_statements expected_outputs tolerance} {
|
||||
foreach db $::test_dbs {
|
||||
puts [format "(%s) %s Running test: %s" $db [string repeat " " [expr {40 - [string length $db]}]] $test_name]
|
||||
set combined_sql [string trim $sql_statements]
|
||||
set actual_output [evaluate_sql $::sqlite_exec $db $combined_sql]
|
||||
set actual_values [split $actual_output "\n"]
|
||||
set expected_values [split $expected_outputs "\n"]
|
||||
|
||||
if {[llength $actual_values] != [llength $expected_values]} {
|
||||
puts "Test FAILED: '$sql_statements'"
|
||||
puts "returned '$actual_output'"
|
||||
puts "expected '$expected_outputs'"
|
||||
exit 1
|
||||
}
|
||||
|
||||
for {set i 0} {$i < [llength $actual_values]} {incr i} {
|
||||
set actual [lindex $actual_values $i]
|
||||
set expected [lindex $expected_values $i]
|
||||
|
||||
if {![within_tolerance $actual $expected $tolerance]} {
|
||||
set lower_bound [expr {$expected - $tolerance}]
|
||||
set upper_bound [expr {$expected + $tolerance}]
|
||||
puts "Test FAILED: '$sql_statements'"
|
||||
puts "returned '$actual'"
|
||||
puts "expected a value within the range \[$lower_bound, $upper_bound\]"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user