diff --git a/COMPAT.md b/COMPAT.md index f167f0521..eebc6581b 100644 --- a/COMPAT.md +++ b/COMPAT.md @@ -427,6 +427,7 @@ Modifiers: | BitOr | Yes | | | Blob | Yes | | | BeginSubrtn | Yes | | +| Cast | Yes | | | Checkpoint | Yes | | | Clear | No | | | Close | Yes | | @@ -554,11 +555,6 @@ Modifiers: | String8 | Yes | | | Subtract | Yes | | | TableLock | No | | -| ToBlob | No | | -| ToInt | No | | -| ToNumeric | No | | -| ToReal | No | | -| ToText | No | | | Trace | No | | | Transaction | Yes | | | VBegin | No | | diff --git a/core/translate/expr.rs b/core/translate/expr.rs index 2d7cdc26b..59cd4945a 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -8,7 +8,7 @@ use super::plan::TableReferences; use crate::function::JsonFunc; use crate::function::{Func, FuncCtx, MathFuncArity, ScalarFunc, VectorFunc}; use crate::functions::datetime; -use crate::schema::{Affinity, Table, Type}; +use crate::schema::{affinity, Affinity, Table, Type}; use crate::util::{exprs_are_equivalent, parse_numeric_literal}; use crate::vdbe::builder::CursorKey; use crate::vdbe::{ @@ -651,24 +651,11 @@ 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_registers(2); - translate_expr(program, referenced_tables, expr, reg_expr, resolver)?; - 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_expr + 1, - }); - program.mark_last_insn_constant(); - program.emit_insn(Insn::Function { - constant_mask: 0, - start_reg: reg_expr, - dest: target_register, - func: FuncCtx { - func: Func::Scalar(ScalarFunc::Cast), - arg_count: 2, - }, + translate_expr(program, referenced_tables, expr, target_register, resolver)?; + let type_affinity = affinity(&type_name.name.to_uppercase()); + program.emit_insn(Insn::Cast { + reg: target_register, + affinity: type_affinity, }); Ok(target_register) } diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index 4d0a55231..f3c9bbd79 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -6651,6 +6651,31 @@ pub fn op_integrity_check( Ok(InsnFunctionStepResult::Step) } +pub fn op_cast( + _program: &Program, + state: &mut ProgramState, + insn: &Insn, + _pager: &Rc, + _mv_store: Option<&Rc>, +) -> Result { + let Insn::Cast { reg, affinity } = insn else { + unreachable!("unexpected Insn {:?}", insn) + }; + + let value = state.registers[*reg].get_owned_value().clone(); + let result = match affinity { + Affinity::Blob => value.exec_cast("BLOB"), + Affinity::Text => value.exec_cast("TEXT"), + Affinity::Numeric => value.exec_cast("NUMERIC"), + Affinity::Integer => value.exec_cast("INTEGER"), + Affinity::Real => value.exec_cast("REAL"), + }; + + state.registers[*reg] = Register::Value(result); + state.pc += 1; + Ok(InsnFunctionStepResult::Step) +} + impl Value { pub fn exec_lower(&self) -> Option { match self { diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs index 1578c2d44..e022b675c 100644 --- a/core/vdbe/explain.rs +++ b/core/vdbe/explain.rs @@ -1609,6 +1609,15 @@ pub fn insn_to_str( 0, format!("r[{}] = data", *dest), ), + Insn::Cast { reg, affinity } => ( + "Cast", + *reg as i32, + 0, + 0, + Value::build_text(""), + 0, + format!("affinity(r[{}]={:?})", *reg, affinity), + ), }; format!( "{:<4} {:<17} {:<4} {:<4} {:<4} {:<13} {:<2} {}", diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs index 92dc66a15..b801caeb8 100644 --- a/core/vdbe/insn.rs +++ b/core/vdbe/insn.rs @@ -706,6 +706,12 @@ pub enum Insn { func: FuncCtx, // P4 }, + /// Cast register P1 to affinity P2 and store in register P1 + Cast { + reg: usize, + affinity: Affinity, + }, + InitCoroutine { yield_reg: usize, jump_on_definition: BranchOffset, @@ -1075,6 +1081,7 @@ impl Insn { Insn::SorterData { .. } => execute::op_sorter_data, Insn::SorterNext { .. } => execute::op_sorter_next, Insn::Function { .. } => execute::op_function, + Insn::Cast { .. } => execute::op_cast, Insn::InitCoroutine { .. } => execute::op_init_coroutine, Insn::EndCoroutine { .. } => execute::op_end_coroutine, Insn::Yield { .. } => execute::op_yield,