diff --git a/core/translate/expr.rs b/core/translate/expr.rs index cc418f47b..3d4f08df5 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -835,15 +835,14 @@ pub fn translate_expr( } ast::Expr::Cast { expr, type_name } => { let type_name = type_name.as_ref().unwrap(); // TODO: why is this optional? - let reg_expr = program.alloc_register(); + let reg_expr = program.alloc_registers(2); translate_expr(program, referenced_tables, expr, reg_expr, resolver)?; - let reg_type = program.alloc_register(); program.emit_insn(Insn::String8 { // we make a comparison against uppercase static strs in the affinity() function, // so we need to make sure we're comparing against the uppercase version, // and it's better to do this once instead of every time we check affinity value: type_name.name.to_uppercase(), - dest: reg_type, + dest: reg_expr + 1, }); program.mark_last_insn_constant(); program.emit_insn(Insn::Function { diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 44b38564b..e4f130c21 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -3484,7 +3484,7 @@ fn exec_cast(value: &OwnedValue, datatype: &str) -> OwnedValue { // then the result is the greatest possible signed integer and if the REAL is less than the least possible signed integer (-9223372036854775808) // then the result is the least possible signed integer. OwnedValue::Float(f) => { - let i = f.floor() as i128; + let i = f.trunc() as i128; if i > i64::MAX as i128 { OwnedValue::Integer(i64::MAX) } else if i < i64::MIN as i128 { diff --git a/testing/scalar-functions.test b/testing/scalar-functions.test index 77694863c..488898b94 100755 --- a/testing/scalar-functions.test +++ b/testing/scalar-functions.test @@ -740,6 +740,21 @@ do_execsql_test cast-float-to-integer { SELECT CAST(123.45 AS INTEGER); } {123} +do_execsql_test cast-float-to-integer-rounding { + SELECT CAST(0.6 AS INTEGER); + SELECT CAST(1.0 AS INTEGER); + SELECT CAST(1.6 AS INTEGER); + SELECT CAST(-0.6 AS INTEGER); + SELECT CAST(-1.0 AS INTEGER); + SELECT CAST(-1.6 AS INTEGER); +} {0 +1 +1 +0 +-1 +-1 +} + do_execsql_test cast-large-float-to-integer { SELECT CAST(9223372036854775808.0 AS INTEGER); } {9223372036854775807} diff --git a/tests/integration/fuzz/mod.rs b/tests/integration/fuzz/mod.rs index b93d88467..a8dcd1d6f 100644 --- a/tests/integration/fuzz/mod.rs +++ b/tests/integration/fuzz/mod.rs @@ -165,6 +165,7 @@ mod tests { "SELECT like('a%', 'a') = 1", "SELECT CASE ( NULL < NULL ) WHEN ( 0 ) THEN ( NULL ) ELSE ( 2.0 ) END;", "SELECT (COALESCE(0, COALESCE(0, 0)));", + "SELECT CAST((1 > 0) AS INTEGER);", ] { let limbo = limbo_exec_row(&limbo_conn, query); let sqlite = sqlite_exec_row(&sqlite_conn, query); @@ -238,6 +239,17 @@ mod tests { .push_str(")") .build(); + let (cast_expr, cast_expr_builder) = g.create_handle(); + cast_expr_builder + .concat(" ") + .push_str("CAST ( (") + .push(expr) + .push_str(") AS ") + // cast to INTEGER/REAL/TEXT types can be added when Limbo will use proper equality semantic between values (e.g. 1 = 1.0) + .push(g.create().choice().options_str(["NUMERIC"]).build()) + .push_str(")") + .build(); + let (case_expr, case_expr_builder) = g.create_handle(); case_expr_builder .concat(" ") @@ -309,6 +321,7 @@ mod tests { expr_builder .choice() + .option_w(cast_expr, 1.0) .option_w(case_expr, 1.0) .option_w(unary_infix_op, 2.0) .option_w(bin_op, 3.0)