//! The virtual database engine (VDBE). //! //! The VDBE is a register-based virtual machine that execute bytecode //! instructions that represent SQL statements. When an application prepares //! an SQL statement, the statement is compiled into a sequence of bytecode //! instructions that perform the needed operations, such as reading or //! writing to a b-tree, sorting, or aggregating data. //! //! The instruction set of the VDBE is similar to SQLite's instruction set, //! but with the exception that bytecodes that perform I/O operations are //! return execution back to the caller instead of blocking. This is because //! Limbo is designed for applications that need high concurrency such as //! serverless runtimes. In addition, asynchronous I/O makes storage //! disaggregation easier. //! //! You can find a full list of SQLite opcodes at: //! //! https://www.sqlite.org/opcode.html pub mod builder; pub mod explain; pub mod insn; pub mod likeop; pub mod sorter; use crate::error::{LimboError, SQLITE_CONSTRAINT_PRIMARYKEY}; use crate::ext::ExtValue; use crate::function::{AggFunc, ExtFunc, FuncCtx, MathFunc, MathFuncArity, ScalarFunc, VectorFunc}; use crate::functions::datetime::{ exec_date, exec_datetime_full, exec_julianday, exec_strftime, exec_time, exec_unixepoch, }; use crate::functions::printf::exec_printf; use crate::info; use crate::pseudo::PseudoCursor; use crate::result::LimboResult; use crate::schema::{affinity, Affinity}; use crate::storage::sqlite3_ondisk::DatabaseHeader; use crate::storage::wal::CheckpointResult; use crate::storage::{btree::BTreeCursor, pager::Pager}; use crate::translate::plan::{ResultSetColumn, TableReference}; use crate::types::{ AggContext, Cursor, CursorResult, ExternalAggState, OwnedValue, Record, SeekKey, SeekOp, }; use crate::util::parse_schema_rows; use crate::vdbe::builder::CursorType; use crate::vdbe::insn::Insn; use crate::vector::{vector32, vector64, vector_distance_cos, vector_extract}; #[cfg(feature = "json")] use crate::{ function::JsonFunc, json::get_json, json::is_json_valid, json::json_array, json::json_array_length, json::json_arrow_extract, json::json_arrow_shift_extract, json::json_error_position, json::json_extract, json::json_object, json::json_patch, json::json_quote, json::json_remove, json::json_set, json::json_type, }; use crate::{resolve_ext_path, Connection, Result, TransactionState, DATABASE_VERSION}; use insn::{ exec_add, exec_and, exec_bit_and, exec_bit_not, exec_bit_or, exec_boolean_not, exec_concat, exec_divide, exec_multiply, exec_or, exec_remainder, exec_shift_left, exec_shift_right, exec_subtract, Cookie, }; use likeop::{construct_like_escape_arg, exec_glob, exec_like_with_escape}; use rand::distributions::{Distribution, Uniform}; use rand::{thread_rng, Rng}; use regex::{Regex, RegexBuilder}; use sorter::Sorter; use std::borrow::BorrowMut; use std::cell::{Cell, RefCell, RefMut}; use std::collections::HashMap; use std::ffi::c_void; use std::num::NonZero; use std::rc::{Rc, Weak}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] /// Represents a target for a jump instruction. /// Stores 32-bit ints to keep the enum word-sized. pub enum BranchOffset { /// A label is a named location in the program. /// If there are references to it, it must always be resolved to an Offset /// via program.resolve_label(). Label(u32), /// An offset is a direct index into the instruction list. Offset(InsnReference), /// A placeholder is a temporary value to satisfy the compiler. /// It must be set later. Placeholder, } impl BranchOffset { /// Returns true if the branch offset is a label. pub fn is_label(&self) -> bool { matches!(self, BranchOffset::Label(_)) } /// Returns true if the branch offset is an offset. pub fn is_offset(&self) -> bool { matches!(self, BranchOffset::Offset(_)) } /// Returns the offset value. Panics if the branch offset is a label or placeholder. pub fn to_offset_int(&self) -> InsnReference { match self { BranchOffset::Label(v) => unreachable!("Unresolved label: {}", v), BranchOffset::Offset(v) => *v, BranchOffset::Placeholder => unreachable!("Unresolved placeholder"), } } /// Returns the label value. Panics if the branch offset is an offset or placeholder. pub fn to_label_value(&self) -> u32 { match self { BranchOffset::Label(v) => *v, BranchOffset::Offset(_) => unreachable!("Offset cannot be converted to label value"), BranchOffset::Placeholder => unreachable!("Unresolved placeholder"), } } /// Returns the branch offset as a signed integer. /// Used in explain output, where we don't want to panic in case we have an unresolved /// label or placeholder. pub fn to_debug_int(&self) -> i32 { match self { BranchOffset::Label(v) => *v as i32, BranchOffset::Offset(v) => *v as i32, BranchOffset::Placeholder => i32::MAX, } } /// Adds an integer value to the branch offset. /// Returns a new branch offset. /// Panics if the branch offset is a label or placeholder. pub fn add>(self, n: N) -> BranchOffset { BranchOffset::Offset(self.to_offset_int() + n.into()) } } pub type CursorID = usize; pub type PageIdx = usize; // Index of insn in list of insns type InsnReference = u32; #[derive(Debug)] pub enum StepResult { Done, IO, Row, Interrupt, Busy, } /// If there is I/O, the instruction is restarted. /// Evaluate a Result>, if IO return Ok(StepResult::IO). macro_rules! return_if_io { ($expr:expr) => { match $expr? { CursorResult::Ok(v) => v, CursorResult::IO => return Ok(StepResult::IO), } }; } macro_rules! call_external_function { ( $func_ptr:expr, $dest_register:expr, $state:expr, $arg_count:expr, $start_reg:expr ) => {{ if $arg_count == 0 { let result_c_value: ExtValue = unsafe { ($func_ptr)(0, std::ptr::null()) }; match OwnedValue::from_ffi(&result_c_value) { Ok(result_ov) => { $state.registers[$dest_register] = result_ov; unsafe { result_c_value.free() }; } Err(e) => { unsafe { result_c_value.free() }; return Err(e); } } } else { let register_slice = &$state.registers[$start_reg..$start_reg + $arg_count]; let mut ext_values: Vec = Vec::with_capacity($arg_count); for ov in register_slice.iter() { let val = ov.to_ffi(); ext_values.push(val); } let argv_ptr = ext_values.as_ptr(); let result_c_value: ExtValue = unsafe { ($func_ptr)($arg_count as i32, argv_ptr) }; match OwnedValue::from_ffi(&result_c_value) { Ok(result_ov) => { $state.registers[$dest_register] = result_ov; unsafe { result_c_value.free() }; } Err(e) => { unsafe { result_c_value.free() }; return Err(e); } } } }}; } struct RegexCache { like: HashMap, glob: HashMap, } impl RegexCache { fn new() -> Self { Self { like: HashMap::new(), glob: HashMap::new(), } } } fn get_cursor_as_table_mut<'long, 'short>( cursors: &'short mut RefMut<'long, Vec>>, cursor_id: CursorID, ) -> &'short mut BTreeCursor { let cursor = cursors .get_mut(cursor_id) .expect("cursor id out of bounds") .as_mut() .expect("cursor not allocated") .as_table_mut(); cursor } fn get_cursor_as_index_mut<'long, 'short>( cursors: &'short mut RefMut<'long, Vec>>, cursor_id: CursorID, ) -> &'short mut BTreeCursor { let cursor = cursors .get_mut(cursor_id) .expect("cursor id out of bounds") .as_mut() .expect("cursor not allocated") .as_index_mut(); cursor } fn get_cursor_as_pseudo_mut<'long, 'short>( cursors: &'short mut RefMut<'long, Vec>>, cursor_id: CursorID, ) -> &'short mut PseudoCursor { let cursor = cursors .get_mut(cursor_id) .expect("cursor id out of bounds") .as_mut() .expect("cursor not allocated") .as_pseudo_mut(); cursor } fn get_cursor_as_sorter_mut<'long, 'short>( cursors: &'short mut RefMut<'long, Vec>>, cursor_id: CursorID, ) -> &'short mut Sorter { let cursor = cursors .get_mut(cursor_id) .expect("cursor id out of bounds") .as_mut() .expect("cursor not allocated") .as_sorter_mut(); cursor } fn get_cursor_as_virtual_mut<'long, 'short>( cursors: &'short mut RefMut<'long, Vec>>, cursor_id: CursorID, ) -> &'short mut VTabOpaqueCursor { let cursor = cursors .get_mut(cursor_id) .expect("cursor id out of bounds") .as_mut() .expect("cursor not allocated") .as_virtual_mut(); cursor } struct Bitfield([u64; N]); impl Bitfield { fn new() -> Self { Self([0; N]) } fn set(&mut self, bit: usize) { assert!(bit < N * 64, "bit out of bounds"); self.0[bit / 64] |= 1 << (bit % 64); } fn unset(&mut self, bit: usize) { assert!(bit < N * 64, "bit out of bounds"); self.0[bit / 64] &= !(1 << (bit % 64)); } fn get(&self, bit: usize) -> bool { assert!(bit < N * 64, "bit out of bounds"); (self.0[bit / 64] & (1 << (bit % 64))) != 0 } } pub struct VTabOpaqueCursor(*mut c_void); impl VTabOpaqueCursor { pub fn new(cursor: *mut c_void) -> Self { Self(cursor) } pub fn as_ptr(&self) -> *mut c_void { self.0 } } /// The program state describes the environment in which the program executes. pub struct ProgramState { pub pc: InsnReference, cursors: RefCell>>, registers: Vec, pub(crate) result_row: Option, last_compare: Option, deferred_seek: Option<(CursorID, CursorID)>, ended_coroutine: Bitfield<4>, // flag to indicate that a coroutine has ended (key is the yield register. currently we assume that the yield register is always between 0-255, YOLO) regex_cache: RegexCache, interrupted: bool, parameters: HashMap, OwnedValue>, } impl ProgramState { pub fn new(max_registers: usize, max_cursors: usize) -> Self { let cursors: RefCell>> = RefCell::new((0..max_cursors).map(|_| None).collect()); let registers = vec![OwnedValue::Null; max_registers]; Self { pc: 0, cursors, registers, result_row: None, last_compare: None, deferred_seek: None, ended_coroutine: Bitfield::new(), regex_cache: RegexCache::new(), interrupted: false, parameters: HashMap::new(), } } pub fn column_count(&self) -> usize { self.registers.len() } pub fn column(&self, i: usize) -> Option { Some(format!("{:?}", self.registers[i])) } pub fn interrupt(&mut self) { self.interrupted = true; } pub fn is_interrupted(&self) -> bool { self.interrupted } pub fn bind_at(&mut self, index: NonZero, value: OwnedValue) { self.parameters.insert(index, value); } pub fn get_parameter(&self, index: NonZero) -> Option<&OwnedValue> { self.parameters.get(&index) } pub fn reset(&mut self) { self.pc = 0; self.cursors.borrow_mut().iter_mut().for_each(|c| *c = None); self.registers .iter_mut() .for_each(|r| *r = OwnedValue::Null); self.last_compare = None; self.deferred_seek = None; self.ended_coroutine.0 = [0; 4]; self.regex_cache.like.clear(); self.interrupted = false; self.parameters.clear(); } } macro_rules! must_be_btree_cursor { ($cursor_id:expr, $cursor_ref:expr, $cursors:expr, $insn_name:expr) => {{ let (_, cursor_type) = $cursor_ref.get($cursor_id).unwrap(); let cursor = match cursor_type { CursorType::BTreeTable(_) => get_cursor_as_table_mut(&mut $cursors, $cursor_id), CursorType::BTreeIndex(_) => get_cursor_as_index_mut(&mut $cursors, $cursor_id), CursorType::Pseudo(_) => panic!("{} on pseudo cursor", $insn_name), CursorType::Sorter => panic!("{} on sorter cursor", $insn_name), CursorType::VirtualTable(_) => panic!("{} on virtual table cursor", $insn_name), }; cursor }}; } #[derive(Debug)] pub struct Program { pub max_registers: usize, pub insns: Vec, pub cursor_ref: Vec<(Option, CursorType)>, pub database_header: Rc>, pub comments: Option>, pub parameters: crate::parameters::Parameters, pub connection: Weak, pub n_change: Cell, pub change_cnt_on: bool, pub result_columns: Vec, pub table_references: Vec, } impl Program { pub fn explain(&self) { println!("addr opcode p1 p2 p3 p4 p5 comment"); println!("---- ----------------- ---- ---- ---- ------------- -- -------"); let mut indent_count: usize = 0; let indent = " "; let mut prev_insn: Option<&Insn> = None; for (addr, insn) in self.insns.iter().enumerate() { indent_count = get_indent_count(indent_count, insn, prev_insn); print_insn( self, addr as InsnReference, insn, indent.repeat(indent_count), ); prev_insn = Some(insn); } } pub fn step(&self, state: &mut ProgramState, pager: Rc) -> Result { loop { if state.is_interrupted() { return Ok(StepResult::Interrupt); } let insn = &self.insns[state.pc as usize]; trace_insn(self, state.pc as InsnReference, insn); match insn { Insn::Init { target_pc } => { assert!(target_pc.is_offset()); state.pc = target_pc.to_offset_int(); } Insn::Add { lhs, rhs, dest } => { state.registers[*dest] = exec_add(&state.registers[*lhs], &state.registers[*rhs]); state.pc += 1; } Insn::Subtract { lhs, rhs, dest } => { state.registers[*dest] = exec_subtract(&state.registers[*lhs], &state.registers[*rhs]); state.pc += 1; } Insn::Multiply { lhs, rhs, dest } => { state.registers[*dest] = exec_multiply(&state.registers[*lhs], &state.registers[*rhs]); state.pc += 1; } Insn::Divide { lhs, rhs, dest } => { state.registers[*dest] = exec_divide(&state.registers[*lhs], &state.registers[*rhs]); state.pc += 1; } Insn::Remainder { lhs, rhs, dest } => { state.registers[*dest] = exec_remainder(&state.registers[*lhs], &state.registers[*rhs]); state.pc += 1; } Insn::BitAnd { lhs, rhs, dest } => { state.registers[*dest] = exec_bit_and(&state.registers[*lhs], &state.registers[*rhs]); state.pc += 1; } Insn::BitOr { lhs, rhs, dest } => { state.registers[*dest] = exec_bit_or(&state.registers[*lhs], &state.registers[*rhs]); state.pc += 1; } Insn::BitNot { reg, dest } => { state.registers[*dest] = exec_bit_not(&state.registers[*reg]); state.pc += 1; } Insn::Checkpoint { database: _, checkpoint_mode: _, dest, } => { let result = self.connection.upgrade().unwrap().checkpoint(); match result { Ok(CheckpointResult { num_wal_frames: num_wal_pages, num_checkpointed_frames: num_checkpointed_pages, }) => { // https://sqlite.org/pragma.html#pragma_wal_checkpoint // 1st col: 1 (checkpoint SQLITE_BUSY) or 0 (not busy). state.registers[*dest] = OwnedValue::Integer(0); // 2nd col: # modified pages written to wal file state.registers[*dest + 1] = OwnedValue::Integer(num_wal_pages as i64); // 3rd col: # pages moved to db after checkpoint state.registers[*dest + 2] = OwnedValue::Integer(num_checkpointed_pages as i64); } Err(_err) => state.registers[*dest] = OwnedValue::Integer(1), } state.pc += 1; } Insn::Null { dest, dest_end } => { if let Some(dest_end) = dest_end { for i in *dest..=*dest_end { state.registers[i] = OwnedValue::Null; } } else { state.registers[*dest] = OwnedValue::Null; } state.pc += 1; } Insn::NullRow { cursor_id } => { let mut cursors = state.cursors.borrow_mut(); let cursor = must_be_btree_cursor!(*cursor_id, self.cursor_ref, cursors, "NullRow"); cursor.set_null_flag(true); state.pc += 1; } Insn::Compare { start_reg_a, start_reg_b, count, } => { let start_reg_a = *start_reg_a; let start_reg_b = *start_reg_b; let count = *count; if start_reg_a + count > start_reg_b { return Err(LimboError::InternalError( "Compare registers overlap".to_string(), )); } let mut cmp = None; for i in 0..count { let a = &state.registers[start_reg_a + i]; let b = &state.registers[start_reg_b + i]; cmp = Some(a.cmp(b)); if cmp != Some(std::cmp::Ordering::Equal) { break; } } state.last_compare = cmp; state.pc += 1; } Insn::Jump { target_pc_lt, target_pc_eq, target_pc_gt, } => { assert!(target_pc_lt.is_offset()); assert!(target_pc_eq.is_offset()); assert!(target_pc_gt.is_offset()); let cmp = state.last_compare.take(); if cmp.is_none() { return Err(LimboError::InternalError( "Jump without compare".to_string(), )); } let target_pc = match cmp.unwrap() { std::cmp::Ordering::Less => *target_pc_lt, std::cmp::Ordering::Equal => *target_pc_eq, std::cmp::Ordering::Greater => *target_pc_gt, }; state.pc = target_pc.to_offset_int(); } Insn::Move { source_reg, dest_reg, count, } => { let source_reg = *source_reg; let dest_reg = *dest_reg; let count = *count; for i in 0..count { state.registers[dest_reg + i] = std::mem::replace( &mut state.registers[source_reg + i], OwnedValue::Null, ); } state.pc += 1; } Insn::IfPos { reg, target_pc, decrement_by, } => { assert!(target_pc.is_offset()); let reg = *reg; let target_pc = *target_pc; match &state.registers[reg] { OwnedValue::Integer(n) if *n > 0 => { state.pc = target_pc.to_offset_int(); state.registers[reg] = OwnedValue::Integer(*n - *decrement_by as i64); } OwnedValue::Integer(_) => { state.pc += 1; } _ => { return Err(LimboError::InternalError( "IfPos: the value in the register is not an integer".into(), )); } } } Insn::NotNull { reg, target_pc } => { assert!(target_pc.is_offset()); let reg = *reg; let target_pc = *target_pc; match &state.registers[reg] { OwnedValue::Null => { state.pc += 1; } _ => { state.pc = target_pc.to_offset_int(); } } } Insn::Eq { lhs, rhs, target_pc, flags, } => { assert!(target_pc.is_offset()); let lhs = *lhs; let rhs = *rhs; let target_pc = *target_pc; let cond = state.registers[lhs] == state.registers[rhs]; let nulleq = flags.has_nulleq(); let jump_if_null = flags.has_jump_if_null(); match (&state.registers[lhs], &state.registers[rhs]) { (_, OwnedValue::Null) | (OwnedValue::Null, _) => { if (nulleq && cond) || (!nulleq && jump_if_null) { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } _ => { if state.registers[lhs] == state.registers[rhs] { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } } } Insn::Ne { lhs, rhs, target_pc, flags, } => { assert!(target_pc.is_offset()); let lhs = *lhs; let rhs = *rhs; let target_pc = *target_pc; let cond = state.registers[lhs] != state.registers[rhs]; let nulleq = flags.has_nulleq(); let jump_if_null = flags.has_jump_if_null(); match (&state.registers[lhs], &state.registers[rhs]) { (_, OwnedValue::Null) | (OwnedValue::Null, _) => { if (nulleq && cond) || (!nulleq && jump_if_null) { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } _ => { if state.registers[lhs] != state.registers[rhs] { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } } } Insn::Lt { lhs, rhs, target_pc, flags, } => { assert!(target_pc.is_offset()); let lhs = *lhs; let rhs = *rhs; let target_pc = *target_pc; let jump_if_null = flags.has_jump_if_null(); match (&state.registers[lhs], &state.registers[rhs]) { (_, OwnedValue::Null) | (OwnedValue::Null, _) => { if jump_if_null { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } _ => { if state.registers[lhs] < state.registers[rhs] { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } } } Insn::Le { lhs, rhs, target_pc, flags, } => { assert!(target_pc.is_offset()); let lhs = *lhs; let rhs = *rhs; let target_pc = *target_pc; let jump_if_null = flags.has_jump_if_null(); match (&state.registers[lhs], &state.registers[rhs]) { (_, OwnedValue::Null) | (OwnedValue::Null, _) => { if jump_if_null { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } _ => { if state.registers[lhs] <= state.registers[rhs] { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } } } Insn::Gt { lhs, rhs, target_pc, flags, } => { assert!(target_pc.is_offset()); let lhs = *lhs; let rhs = *rhs; let target_pc = *target_pc; let jump_if_null = flags.has_jump_if_null(); match (&state.registers[lhs], &state.registers[rhs]) { (_, OwnedValue::Null) | (OwnedValue::Null, _) => { if jump_if_null { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } _ => { if state.registers[lhs] > state.registers[rhs] { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } } } Insn::Ge { lhs, rhs, target_pc, flags, } => { assert!(target_pc.is_offset()); let lhs = *lhs; let rhs = *rhs; let target_pc = *target_pc; let jump_if_null = flags.has_jump_if_null(); match (&state.registers[lhs], &state.registers[rhs]) { (_, OwnedValue::Null) | (OwnedValue::Null, _) => { if jump_if_null { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } _ => { if state.registers[lhs] >= state.registers[rhs] { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } } } Insn::If { reg, target_pc, jump_if_null, } => { assert!(target_pc.is_offset()); if exec_if(&state.registers[*reg], *jump_if_null, false) { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } Insn::IfNot { reg, target_pc, jump_if_null, } => { assert!(target_pc.is_offset()); if exec_if(&state.registers[*reg], *jump_if_null, true) { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } Insn::OpenReadAsync { cursor_id, root_page, } => { let (_, cursor_type) = self.cursor_ref.get(*cursor_id).unwrap(); let cursor = BTreeCursor::new(pager.clone(), *root_page); let mut cursors = state.cursors.borrow_mut(); match cursor_type { CursorType::BTreeTable(_) => { cursors .get_mut(*cursor_id) .unwrap() .replace(Cursor::new_table(cursor)); } CursorType::BTreeIndex(_) => { cursors .get_mut(*cursor_id) .unwrap() .replace(Cursor::new_index(cursor)); } CursorType::Pseudo(_) => { panic!("OpenReadAsync on pseudo cursor"); } CursorType::Sorter => { panic!("OpenReadAsync on sorter cursor"); } CursorType::VirtualTable(_) => { panic!("OpenReadAsync on virtual table cursor, use Insn::VOpenAsync instead"); } } state.pc += 1; } Insn::OpenReadAwait => { state.pc += 1; } Insn::VOpenAsync { cursor_id } => { let (_, cursor_type) = self.cursor_ref.get(*cursor_id).unwrap(); let CursorType::VirtualTable(virtual_table) = cursor_type else { panic!("VOpenAsync on non-virtual table cursor"); }; let cursor = virtual_table.open(); state .cursors .borrow_mut() .insert(*cursor_id, Some(Cursor::Virtual(cursor))); state.pc += 1; } Insn::VOpenAwait => { state.pc += 1; } Insn::VFilter { cursor_id, pc_if_empty, arg_count, args_reg, } => { let (_, cursor_type) = self.cursor_ref.get(*cursor_id).unwrap(); let CursorType::VirtualTable(virtual_table) = cursor_type else { panic!("VFilter on non-virtual table cursor"); }; let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_virtual_mut(&mut cursors, *cursor_id); let mut args = Vec::new(); for i in 0..*arg_count { args.push(state.registers[args_reg + i].clone()); } let has_rows = virtual_table.filter(cursor, *arg_count, args)?; if !has_rows { state.pc = pc_if_empty.to_offset_int(); } else { state.pc += 1; } } Insn::VColumn { cursor_id, column, dest, } => { let (_, cursor_type) = self.cursor_ref.get(*cursor_id).unwrap(); let CursorType::VirtualTable(virtual_table) = cursor_type else { panic!("VColumn on non-virtual table cursor"); }; let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_virtual_mut(&mut cursors, *cursor_id); state.registers[*dest] = virtual_table.column(cursor, *column)?; state.pc += 1; } Insn::VNext { cursor_id, pc_if_next, } => { let (_, cursor_type) = self.cursor_ref.get(*cursor_id).unwrap(); let CursorType::VirtualTable(virtual_table) = cursor_type else { panic!("VNextAsync on non-virtual table cursor"); }; let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_virtual_mut(&mut cursors, *cursor_id); let has_more = virtual_table.next(cursor)?; if has_more { state.pc = pc_if_next.to_offset_int(); } else { state.pc += 1; } } Insn::OpenPseudo { cursor_id, content_reg: _, num_fields: _, } => { let mut cursors = state.cursors.borrow_mut(); let cursor = PseudoCursor::new(); cursors .get_mut(*cursor_id) .unwrap() .replace(Cursor::new_pseudo(cursor)); state.pc += 1; } Insn::RewindAsync { cursor_id } => { let mut cursors = state.cursors.borrow_mut(); let cursor = must_be_btree_cursor!(*cursor_id, self.cursor_ref, cursors, "RewindAsync"); return_if_io!(cursor.rewind()); state.pc += 1; } Insn::LastAsync { cursor_id } => { let mut cursors = state.cursors.borrow_mut(); let cursor = must_be_btree_cursor!(*cursor_id, self.cursor_ref, cursors, "LastAsync"); return_if_io!(cursor.last()); state.pc += 1; } Insn::LastAwait { cursor_id, pc_if_empty, } => { assert!(pc_if_empty.is_offset()); let mut cursors = state.cursors.borrow_mut(); let cursor = must_be_btree_cursor!(*cursor_id, self.cursor_ref, cursors, "LastAwait"); cursor.wait_for_completion()?; if cursor.is_empty() { state.pc = pc_if_empty.to_offset_int(); } else { state.pc += 1; } } Insn::RewindAwait { cursor_id, pc_if_empty, } => { assert!(pc_if_empty.is_offset()); let mut cursors = state.cursors.borrow_mut(); let cursor = must_be_btree_cursor!(*cursor_id, self.cursor_ref, cursors, "RewindAwait"); cursor.wait_for_completion()?; if cursor.is_empty() { state.pc = pc_if_empty.to_offset_int(); } else { state.pc += 1; } } Insn::Column { cursor_id, column, dest, } => { let mut cursors = state.cursors.borrow_mut(); if let Some((index_cursor_id, table_cursor_id)) = state.deferred_seek.take() { let index_cursor = get_cursor_as_index_mut(&mut cursors, index_cursor_id); let rowid = index_cursor.rowid()?; let table_cursor = get_cursor_as_table_mut(&mut cursors, table_cursor_id); match table_cursor.seek(SeekKey::TableRowId(rowid.unwrap()), SeekOp::EQ)? { CursorResult::Ok(_) => {} CursorResult::IO => { state.deferred_seek = Some((index_cursor_id, table_cursor_id)); return Ok(StepResult::IO); } } } let (_, cursor_type) = self.cursor_ref.get(*cursor_id).unwrap(); match cursor_type { CursorType::BTreeTable(_) | CursorType::BTreeIndex(_) => { let cursor = must_be_btree_cursor!( *cursor_id, self.cursor_ref, cursors, "Column" ); let record = cursor.record()?; if let Some(record) = record.as_ref() { state.registers[*dest] = if cursor.get_null_flag() { OwnedValue::Null } else { record.get_value(*column).clone() }; } else { state.registers[*dest] = OwnedValue::Null; } } CursorType::Sorter => { let cursor = get_cursor_as_sorter_mut(&mut cursors, *cursor_id); if let Some(record) = cursor.record() { state.registers[*dest] = record.get_value(*column).clone(); } else { state.registers[*dest] = OwnedValue::Null; } } CursorType::Pseudo(_) => { let cursor = get_cursor_as_pseudo_mut(&mut cursors, *cursor_id); if let Some(record) = cursor.record() { state.registers[*dest] = record.get_value(*column).clone(); } else { state.registers[*dest] = OwnedValue::Null; } } CursorType::VirtualTable(_) => { panic!( "Insn::Column on virtual table cursor, use Insn::VColumn instead" ); } } state.pc += 1; } Insn::MakeRecord { start_reg, count, dest_reg, } => { let record = make_owned_record(&state.registers, start_reg, count); state.registers[*dest_reg] = OwnedValue::Record(record); state.pc += 1; } Insn::ResultRow { start_reg, count } => { let record = make_owned_record(&state.registers, start_reg, count); state.result_row = Some(record); state.pc += 1; return Ok(StepResult::Row); } Insn::NextAsync { cursor_id } => { let mut cursors = state.cursors.borrow_mut(); let cursor = must_be_btree_cursor!(*cursor_id, self.cursor_ref, cursors, "NextAsync"); cursor.set_null_flag(false); return_if_io!(cursor.next()); state.pc += 1; } Insn::PrevAsync { cursor_id } => { let mut cursors = state.cursors.borrow_mut(); let cursor = must_be_btree_cursor!(*cursor_id, self.cursor_ref, cursors, "PrevAsync"); cursor.set_null_flag(false); return_if_io!(cursor.prev()); state.pc += 1; } Insn::PrevAwait { cursor_id, pc_if_next, } => { let mut cursors = state.cursors.borrow_mut(); assert!(pc_if_next.is_offset()); let cursor = must_be_btree_cursor!(*cursor_id, self.cursor_ref, cursors, "PrevAwait"); cursor.wait_for_completion()?; if !cursor.is_empty() { state.pc = pc_if_next.to_offset_int(); } else { state.pc += 1; } } Insn::NextAwait { cursor_id, pc_if_next, } => { assert!(pc_if_next.is_offset()); let mut cursors = state.cursors.borrow_mut(); let cursor = must_be_btree_cursor!(*cursor_id, self.cursor_ref, cursors, "NextAwait"); cursor.wait_for_completion()?; if !cursor.is_empty() { state.pc = pc_if_next.to_offset_int(); } else { state.pc += 1; } } Insn::Halt { err_code, description, } => { match *err_code { 0 => {} SQLITE_CONSTRAINT_PRIMARYKEY => { return Err(LimboError::Constraint(format!( "UNIQUE constraint failed: {} (19)", description ))); } _ => { return Err(LimboError::Constraint(format!( "undocumented halt error code {}", description ))); } } return self.halt(pager); } Insn::Transaction { write } => { let connection = self.connection.upgrade().unwrap(); let current_state = connection.transaction_state.borrow().clone(); let (new_transaction_state, updated) = match (¤t_state, write) { (TransactionState::Write, true) => (TransactionState::Write, false), (TransactionState::Write, false) => (TransactionState::Write, false), (TransactionState::Read, true) => (TransactionState::Write, true), (TransactionState::Read, false) => (TransactionState::Read, false), (TransactionState::None, true) => (TransactionState::Write, true), (TransactionState::None, false) => (TransactionState::Read, true), }; if updated && matches!(current_state, TransactionState::None) { if let LimboResult::Busy = pager.begin_read_tx()? { tracing::trace!("begin_read_tx busy"); return Ok(StepResult::Busy); } } if updated && matches!(new_transaction_state, TransactionState::Write) { if let LimboResult::Busy = pager.begin_write_tx()? { tracing::trace!("begin_write_tx busy"); return Ok(StepResult::Busy); } } if updated { connection .transaction_state .replace(new_transaction_state.clone()); } state.pc += 1; } Insn::AutoCommit { auto_commit, rollback, } => { let conn = self.connection.upgrade().unwrap(); if *auto_commit != *conn.auto_commit.borrow() { if *rollback { todo!("Rollback is not implemented"); } else { conn.auto_commit.replace(*auto_commit); } } else { if !*auto_commit { return Err(LimboError::TxError( "cannot start a transaction within a transaction".to_string(), )); } else if *rollback { return Err(LimboError::TxError( "cannot rollback - no transaction is active".to_string(), )); } else { return Err(LimboError::TxError( "cannot commit - no transaction is active".to_string(), )); } } return self.halt(pager); } Insn::Goto { target_pc } => { assert!(target_pc.is_offset()); state.pc = target_pc.to_offset_int(); } Insn::Gosub { target_pc, return_reg, } => { assert!(target_pc.is_offset()); state.registers[*return_reg] = OwnedValue::Integer((state.pc + 1) as i64); state.pc = target_pc.to_offset_int(); } Insn::Return { return_reg } => { if let OwnedValue::Integer(pc) = state.registers[*return_reg] { let pc: u32 = pc .try_into() .unwrap_or_else(|_| panic!("Return register is negative: {}", pc)); state.pc = pc; } else { return Err(LimboError::InternalError( "Return register is not an integer".to_string(), )); } } Insn::Integer { value, dest } => { state.registers[*dest] = OwnedValue::Integer(*value); state.pc += 1; } Insn::Real { value, dest } => { state.registers[*dest] = OwnedValue::Float(*value); state.pc += 1; } Insn::RealAffinity { register } => { if let OwnedValue::Integer(i) = &state.registers[*register] { state.registers[*register] = OwnedValue::Float(*i as f64); }; state.pc += 1; } Insn::String8 { value, dest } => { state.registers[*dest] = OwnedValue::build_text(value); state.pc += 1; } Insn::Blob { value, dest } => { state.registers[*dest] = OwnedValue::Blob(Rc::new(value.clone())); state.pc += 1; } Insn::RowId { cursor_id, dest } => { let mut cursors = state.cursors.borrow_mut(); if let Some((index_cursor_id, table_cursor_id)) = state.deferred_seek.take() { let index_cursor = get_cursor_as_index_mut(&mut cursors, index_cursor_id); let rowid = index_cursor.rowid()?; let table_cursor = get_cursor_as_table_mut(&mut cursors, table_cursor_id); match table_cursor.seek(SeekKey::TableRowId(rowid.unwrap()), SeekOp::EQ)? { CursorResult::Ok(_) => {} CursorResult::IO => { state.deferred_seek = Some((index_cursor_id, table_cursor_id)); return Ok(StepResult::IO); } } } let cursor = get_cursor_as_table_mut(&mut cursors, *cursor_id); if let Some(ref rowid) = cursor.rowid()? { state.registers[*dest] = OwnedValue::Integer(*rowid as i64); } else { state.registers[*dest] = OwnedValue::Null; } state.pc += 1; } Insn::SeekRowid { cursor_id, src_reg, target_pc, } => { assert!(target_pc.is_offset()); let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_table_mut(&mut cursors, *cursor_id); let rowid = match &state.registers[*src_reg] { OwnedValue::Integer(rowid) => *rowid as u64, OwnedValue::Null => { state.pc = target_pc.to_offset_int(); continue; } other => { return Err(LimboError::InternalError( format!("SeekRowid: the value in the register is not an integer or NULL: {}", other) )); } }; let found = return_if_io!(cursor.seek(SeekKey::TableRowId(rowid), SeekOp::EQ)); if !found { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } Insn::DeferredSeek { index_cursor_id, table_cursor_id, } => { state.deferred_seek = Some((*index_cursor_id, *table_cursor_id)); state.pc += 1; } Insn::SeekGE { cursor_id, start_reg, num_regs, target_pc, is_index, } => { assert!(target_pc.is_offset()); let mut cursors = state.cursors.borrow_mut(); if *is_index { let cursor = get_cursor_as_index_mut(&mut cursors, *cursor_id); let record_from_regs: Record = make_owned_record(&state.registers, start_reg, num_regs); let found = return_if_io!( cursor.seek(SeekKey::IndexKey(&record_from_regs), SeekOp::GE) ); if !found { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } else { let cursor = get_cursor_as_table_mut(&mut cursors, *cursor_id); let rowid = match &state.registers[*start_reg] { OwnedValue::Null => { // All integer values are greater than null so we just rewind the cursor return_if_io!(cursor.rewind()); state.pc += 1; continue; } OwnedValue::Integer(rowid) => *rowid as u64, _ => { return Err(LimboError::InternalError( "SeekGE: the value in the register is not an integer".into(), )); } }; let found = return_if_io!(cursor.seek(SeekKey::TableRowId(rowid), SeekOp::GE)); if !found { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } } Insn::SeekGT { cursor_id, start_reg, num_regs, target_pc, is_index, } => { assert!(target_pc.is_offset()); let mut cursors = state.cursors.borrow_mut(); if *is_index { let cursor = get_cursor_as_index_mut(&mut cursors, *cursor_id); let record_from_regs: Record = make_owned_record(&state.registers, start_reg, num_regs); let found = return_if_io!( cursor.seek(SeekKey::IndexKey(&record_from_regs), SeekOp::GT) ); if !found { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } else { let cursor = get_cursor_as_table_mut(&mut cursors, *cursor_id); let rowid = match &state.registers[*start_reg] { OwnedValue::Null => { // All integer values are greater than null so we just rewind the cursor return_if_io!(cursor.rewind()); state.pc += 1; continue; } OwnedValue::Integer(rowid) => *rowid as u64, _ => { return Err(LimboError::InternalError( "SeekGT: the value in the register is not an integer".into(), )); } }; let found = return_if_io!(cursor.seek(SeekKey::TableRowId(rowid), SeekOp::GT)); if !found { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } } Insn::IdxGE { cursor_id, start_reg, num_regs, target_pc, } => { assert!(target_pc.is_offset()); let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_index_mut(&mut cursors, *cursor_id); let record_from_regs: Record = make_owned_record(&state.registers, start_reg, num_regs); if let Some(ref idx_record) = *cursor.record()? { // Compare against the same number of values if idx_record.get_values()[..record_from_regs.len()] >= record_from_regs.get_values()[..] { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } else { state.pc = target_pc.to_offset_int(); }; } Insn::IdxLE { cursor_id, start_reg, num_regs, target_pc, } => { assert!(target_pc.is_offset()); let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_index_mut(&mut cursors, *cursor_id); let record_from_regs: Record = make_owned_record(&state.registers, start_reg, num_regs); if let Some(ref idx_record) = *cursor.record()? { // Compare against the same number of values if idx_record.get_values()[..record_from_regs.len()] <= record_from_regs.get_values()[..] { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } else { state.pc = target_pc.to_offset_int(); }; } Insn::IdxGT { cursor_id, start_reg, num_regs, target_pc, } => { assert!(target_pc.is_offset()); let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_index_mut(&mut cursors, *cursor_id); let record_from_regs: Record = make_owned_record(&state.registers, start_reg, num_regs); if let Some(ref idx_record) = *cursor.record()? { // Compare against the same number of values if idx_record.get_values()[..record_from_regs.len()] > record_from_regs.get_values()[..] { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } else { state.pc = target_pc.to_offset_int(); }; } Insn::IdxLT { cursor_id, start_reg, num_regs, target_pc, } => { assert!(target_pc.is_offset()); let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_index_mut(&mut cursors, *cursor_id); let record_from_regs: Record = make_owned_record(&state.registers, start_reg, num_regs); if let Some(ref idx_record) = *cursor.record()? { // Compare against the same number of values if idx_record.get_values()[..record_from_regs.len()] < record_from_regs.get_values()[..] { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } else { state.pc = target_pc.to_offset_int(); }; } Insn::DecrJumpZero { reg, target_pc } => { assert!(target_pc.is_offset()); match state.registers[*reg] { OwnedValue::Integer(n) => { let n = n - 1; if n == 0 { state.pc = target_pc.to_offset_int(); } else { state.registers[*reg] = OwnedValue::Integer(n); state.pc += 1; } } _ => unreachable!("DecrJumpZero on non-integer register"), } } Insn::AggStep { acc_reg, col, delimiter, func, } => { if let OwnedValue::Null = &state.registers[*acc_reg] { state.registers[*acc_reg] = match func { AggFunc::Avg => OwnedValue::Agg(Box::new(AggContext::Avg( OwnedValue::Float(0.0), OwnedValue::Integer(0), ))), AggFunc::Sum => { OwnedValue::Agg(Box::new(AggContext::Sum(OwnedValue::Null))) } AggFunc::Total => { // The result of total() is always a floating point value. // No overflow error is ever raised if any prior input was a floating point value. // Total() never throws an integer overflow. OwnedValue::Agg(Box::new(AggContext::Sum(OwnedValue::Float(0.0)))) } AggFunc::Count | AggFunc::Count0 => { OwnedValue::Agg(Box::new(AggContext::Count(OwnedValue::Integer(0)))) } AggFunc::Max => { let col = state.registers[*col].clone(); match col { OwnedValue::Integer(_) => { OwnedValue::Agg(Box::new(AggContext::Max(None))) } OwnedValue::Float(_) => { OwnedValue::Agg(Box::new(AggContext::Max(None))) } OwnedValue::Text(_) => { OwnedValue::Agg(Box::new(AggContext::Max(None))) } _ => { unreachable!(); } } } AggFunc::Min => { let col = state.registers[*col].clone(); match col { OwnedValue::Integer(_) => { OwnedValue::Agg(Box::new(AggContext::Min(None))) } OwnedValue::Float(_) => { OwnedValue::Agg(Box::new(AggContext::Min(None))) } OwnedValue::Text(_) => { OwnedValue::Agg(Box::new(AggContext::Min(None))) } _ => { unreachable!(); } } } AggFunc::GroupConcat | AggFunc::StringAgg => OwnedValue::Agg(Box::new( AggContext::GroupConcat(OwnedValue::build_text("")), )), AggFunc::External(func) => match func.as_ref() { ExtFunc::Aggregate { init, step, finalize, argc, } => OwnedValue::Agg(Box::new(AggContext::External( ExternalAggState { state: unsafe { (init)() }, argc: *argc, step_fn: *step, finalize_fn: *finalize, finalized_value: None, }, ))), _ => unreachable!("scalar function called in aggregate context"), }, }; } match func { AggFunc::Avg => { let col = state.registers[*col].clone(); let OwnedValue::Agg(agg) = state.registers[*acc_reg].borrow_mut() else { unreachable!(); }; let AggContext::Avg(acc, count) = agg.borrow_mut() else { unreachable!(); }; *acc += col; *count += 1; } AggFunc::Sum | AggFunc::Total => { let col = state.registers[*col].clone(); let OwnedValue::Agg(agg) = state.registers[*acc_reg].borrow_mut() else { unreachable!(); }; let AggContext::Sum(acc) = agg.borrow_mut() else { unreachable!(); }; *acc += col; } AggFunc::Count | AggFunc::Count0 => { if matches!(&state.registers[*acc_reg], OwnedValue::Null) { state.registers[*acc_reg] = OwnedValue::Agg(Box::new( AggContext::Count(OwnedValue::Integer(0)), )); } let OwnedValue::Agg(agg) = state.registers[*acc_reg].borrow_mut() else { unreachable!(); }; let AggContext::Count(count) = agg.borrow_mut() else { unreachable!(); }; *count += 1; } AggFunc::Max => { let col = state.registers[*col].clone(); let OwnedValue::Agg(agg) = state.registers[*acc_reg].borrow_mut() else { unreachable!(); }; let AggContext::Max(acc) = agg.borrow_mut() else { unreachable!(); }; match (acc.as_mut(), col) { (None, value) => { *acc = Some(value); } ( Some(OwnedValue::Integer(ref mut current_max)), OwnedValue::Integer(value), ) => { if value > *current_max { *current_max = value; } } ( Some(OwnedValue::Float(ref mut current_max)), OwnedValue::Float(value), ) => { if value > *current_max { *current_max = value; } } ( Some(OwnedValue::Text(ref mut current_max)), OwnedValue::Text(value), ) => { if value.value > current_max.value { *current_max = value; } } _ => { eprintln!("Unexpected types in max aggregation"); } } } AggFunc::Min => { let col = state.registers[*col].clone(); let OwnedValue::Agg(agg) = state.registers[*acc_reg].borrow_mut() else { unreachable!(); }; let AggContext::Min(acc) = agg.borrow_mut() else { unreachable!(); }; match (acc.as_mut(), col) { (None, value) => { *acc.borrow_mut() = Some(value); } ( Some(OwnedValue::Integer(ref mut current_min)), OwnedValue::Integer(value), ) => { if value < *current_min { *current_min = value; } } ( Some(OwnedValue::Float(ref mut current_min)), OwnedValue::Float(value), ) => { if value < *current_min { *current_min = value; } } ( Some(OwnedValue::Text(ref mut current_min)), OwnedValue::Text(text), ) => { if text.value < current_min.value { *current_min = text; } } _ => { eprintln!("Unexpected types in min aggregation"); } } } AggFunc::GroupConcat | AggFunc::StringAgg => { let col = state.registers[*col].clone(); let delimiter = state.registers[*delimiter].clone(); let OwnedValue::Agg(agg) = state.registers[*acc_reg].borrow_mut() else { unreachable!(); }; let AggContext::GroupConcat(acc) = agg.borrow_mut() else { unreachable!(); }; if acc.to_string().is_empty() { *acc = col; } else { *acc += delimiter; *acc += col; } } AggFunc::External(_) => { let (step_fn, state_ptr, argc) = { let OwnedValue::Agg(agg) = &state.registers[*acc_reg] else { unreachable!(); }; let AggContext::External(agg_state) = agg.as_ref() else { unreachable!(); }; (agg_state.step_fn, agg_state.state, agg_state.argc) }; if argc == 0 { unsafe { step_fn(state_ptr, 0, std::ptr::null()) }; } else { let register_slice = &state.registers[*col..*col + argc]; let mut ext_values: Vec = Vec::with_capacity(argc); for ov in register_slice.iter() { ext_values.push(ov.to_ffi()); } let argv_ptr = ext_values.as_ptr(); unsafe { step_fn(state_ptr, argc as i32, argv_ptr) }; } } }; state.pc += 1; } Insn::AggFinal { register, func } => { match state.registers[*register].borrow_mut() { OwnedValue::Agg(agg) => match func { AggFunc::Avg => { let AggContext::Avg(acc, count) = agg.borrow_mut() else { unreachable!(); }; *acc /= count.clone(); } AggFunc::Sum | AggFunc::Total => {} AggFunc::Count | AggFunc::Count0 => {} AggFunc::Max => {} AggFunc::Min => {} AggFunc::GroupConcat | AggFunc::StringAgg => {} AggFunc::External(_) => { agg.compute_external()?; } }, OwnedValue::Null => { // when the set is empty match func { AggFunc::Total => { state.registers[*register] = OwnedValue::Float(0.0); } AggFunc::Count | AggFunc::Count0 => { state.registers[*register] = OwnedValue::Integer(0); } _ => {} } } _ => { unreachable!(); } }; state.pc += 1; } Insn::SorterOpen { cursor_id, columns: _, order, } => { let order = order .get_values() .iter() .map(|v| match v { OwnedValue::Integer(i) => *i == 0, _ => unreachable!(), }) .collect(); let cursor = Sorter::new(order); let mut cursors = state.cursors.borrow_mut(); cursors .get_mut(*cursor_id) .unwrap() .replace(Cursor::new_sorter(cursor)); state.pc += 1; } Insn::SorterData { cursor_id, dest_reg, pseudo_cursor, } => { let mut cursors = state.cursors.borrow_mut(); let sorter_cursor = get_cursor_as_sorter_mut(&mut cursors, *cursor_id); let record = match sorter_cursor.record() { Some(record) => record.clone(), None => { state.pc += 1; continue; } }; state.registers[*dest_reg] = OwnedValue::Record(record.clone()); let pseudo_cursor = get_cursor_as_pseudo_mut(&mut cursors, *pseudo_cursor); pseudo_cursor.insert(record); state.pc += 1; } Insn::SorterInsert { cursor_id, record_reg, } => { let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_sorter_mut(&mut cursors, *cursor_id); let record = match &state.registers[*record_reg] { OwnedValue::Record(record) => record, _ => unreachable!("SorterInsert on non-record register"), }; cursor.insert(record); state.pc += 1; } Insn::SorterSort { cursor_id, pc_if_empty, } => { let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_sorter_mut(&mut cursors, *cursor_id); if cursor.is_empty() { state.pc = pc_if_empty.to_offset_int(); } else { cursor.sort(); state.pc += 1; } } Insn::SorterNext { cursor_id, pc_if_next, } => { assert!(pc_if_next.is_offset()); let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_sorter_mut(&mut cursors, *cursor_id); cursor.next(); if cursor.has_more() { state.pc = pc_if_next.to_offset_int(); } else { state.pc += 1; } } Insn::Function { constant_mask, func, start_reg, dest, } => { let arg_count = func.arg_count; match &func.func { #[cfg(feature = "json")] crate::function::Func::Json(json_func) => match json_func { JsonFunc::Json => { let json_value = &state.registers[*start_reg]; let json_str = get_json(json_value, None); match json_str { Ok(json) => state.registers[*dest] = json, Err(e) => return Err(e), } } JsonFunc::JsonArray | JsonFunc::JsonObject => { let reg_values = &state.registers[*start_reg..*start_reg + arg_count]; let json_func = match json_func { JsonFunc::JsonArray => json_array, JsonFunc::JsonObject => json_object, _ => unreachable!(), }; let json_result = json_func(reg_values); match json_result { Ok(json) => state.registers[*dest] = json, Err(e) => return Err(e), } } JsonFunc::JsonExtract => { let result = match arg_count { 0 => json_extract(&OwnedValue::Null, &[]), _ => { let val = &state.registers[*start_reg]; let reg_values = &state.registers [*start_reg + 1..*start_reg + arg_count]; json_extract(val, reg_values) } }; match result { Ok(json) => state.registers[*dest] = json, Err(e) => return Err(e), } } JsonFunc::JsonArrowExtract | JsonFunc::JsonArrowShiftExtract => { assert_eq!(arg_count, 2); let json = &state.registers[*start_reg]; let path = &state.registers[*start_reg + 1]; let json_func = match json_func { JsonFunc::JsonArrowExtract => json_arrow_extract, JsonFunc::JsonArrowShiftExtract => json_arrow_shift_extract, _ => unreachable!(), }; let json_str = json_func(json, path); match json_str { Ok(json) => state.registers[*dest] = json, Err(e) => return Err(e), } } JsonFunc::JsonArrayLength | JsonFunc::JsonType => { let json_value = &state.registers[*start_reg]; let path_value = if arg_count > 1 { Some(&state.registers[*start_reg + 1]) } else { None }; let func_result = match json_func { JsonFunc::JsonArrayLength => { json_array_length(json_value, path_value) } JsonFunc::JsonType => json_type(json_value, path_value), _ => unreachable!(), }; match func_result { Ok(result) => state.registers[*dest] = result, Err(e) => return Err(e), } } JsonFunc::JsonErrorPosition => { let json_value = &state.registers[*start_reg]; match json_error_position(json_value) { Ok(pos) => state.registers[*dest] = pos, Err(e) => return Err(e), } } JsonFunc::JsonValid => { let json_value = &state.registers[*start_reg]; state.registers[*dest] = is_json_valid(json_value)?; } JsonFunc::JsonPatch => { assert_eq!(arg_count, 2); assert!(*start_reg + 1 < state.registers.len()); let target = &state.registers[*start_reg]; let patch = &state.registers[*start_reg + 1]; state.registers[*dest] = json_patch(target, patch)?; } JsonFunc::JsonRemove => { state.registers[*dest] = json_remove( &state.registers[*start_reg..*start_reg + arg_count], )?; } JsonFunc::JsonPretty => { let json_value = &state.registers[*start_reg]; let indent = if arg_count > 1 { Some(&state.registers[*start_reg + 1]) } else { None }; // Blob should be converted to Ascii in a lossy way // However, Rust strings uses utf-8 // so the behavior at the moment is slightly different // To the way blobs are parsed here in SQLite. let indent = match indent { Some(value) => match value { OwnedValue::Text(text) => text.as_str(), OwnedValue::Integer(val) => &val.to_string(), OwnedValue::Float(val) => &val.to_string(), OwnedValue::Blob(val) => &String::from_utf8_lossy(val), OwnedValue::Agg(ctx) => match ctx.final_value() { OwnedValue::Text(text) => text.as_str(), OwnedValue::Integer(val) => &val.to_string(), OwnedValue::Float(val) => &val.to_string(), OwnedValue::Blob(val) => &String::from_utf8_lossy(val), _ => " ", }, _ => " ", }, // If the second argument is omitted or is NULL, then indentation is four spaces per level None => " ", }; let json_str = get_json(json_value, Some(indent))?; state.registers[*dest] = json_str; } JsonFunc::JsonSet => { let reg_values = &state.registers[*start_reg + 1..*start_reg + arg_count]; let json_result = json_set(&state.registers[*start_reg], reg_values); match json_result { Ok(json) => state.registers[*dest] = json, Err(e) => return Err(e), } } JsonFunc::JsonQuote => { let json_value = &state.registers[*start_reg]; match json_quote(json_value) { Ok(result) => state.registers[*dest] = result, Err(e) => return Err(e), } } }, crate::function::Func::Scalar(scalar_func) => match scalar_func { ScalarFunc::Cast => { assert_eq!(arg_count, 2); assert!(*start_reg + 1 < state.registers.len()); let reg_value_argument = state.registers[*start_reg].clone(); let OwnedValue::Text(reg_value_type) = state.registers[*start_reg + 1].clone() else { unreachable!("Cast with non-text type"); }; let result = exec_cast(®_value_argument, ®_value_type.as_str()); state.registers[*dest] = result; } ScalarFunc::Changes => { let res = &self.connection.upgrade().unwrap().last_change; let changes = res.get(); state.registers[*dest] = OwnedValue::Integer(changes); } ScalarFunc::Char => { let reg_values = state.registers[*start_reg..*start_reg + arg_count].to_vec(); state.registers[*dest] = exec_char(reg_values); } ScalarFunc::Coalesce => {} ScalarFunc::Concat => { let result = exec_concat_strings( &state.registers[*start_reg..*start_reg + arg_count], ); state.registers[*dest] = result; } ScalarFunc::ConcatWs => { let result = exec_concat_ws( &state.registers[*start_reg..*start_reg + arg_count], ); state.registers[*dest] = result; } ScalarFunc::Glob => { let pattern = &state.registers[*start_reg]; let text = &state.registers[*start_reg + 1]; let result = match (pattern, text) { (OwnedValue::Text(pattern), OwnedValue::Text(text)) => { let cache = if *constant_mask > 0 { Some(&mut state.regex_cache.glob) } else { None }; OwnedValue::Integer(exec_glob( cache, &pattern.as_str(), &text.as_str(), ) as i64) } _ => { unreachable!("Like on non-text registers"); } }; state.registers[*dest] = result; } ScalarFunc::IfNull => {} ScalarFunc::Iif => {} ScalarFunc::Instr => { let reg_value = &state.registers[*start_reg]; let pattern_value = &state.registers[*start_reg + 1]; let result = exec_instr(reg_value, pattern_value); state.registers[*dest] = result; } ScalarFunc::LastInsertRowid => { if let Some(conn) = self.connection.upgrade() { state.registers[*dest] = OwnedValue::Integer(conn.last_insert_rowid() as i64); } else { state.registers[*dest] = OwnedValue::Null; } } ScalarFunc::Like => { let pattern = &state.registers[*start_reg]; let text = &state.registers[*start_reg + 1]; let result = match (pattern, text) { (OwnedValue::Text(pattern), OwnedValue::Text(text)) if arg_count == 3 => { let escape = match construct_like_escape_arg( &state.registers[*start_reg + 2], ) { Ok(x) => x, Err(e) => return Err(e), }; OwnedValue::Integer(exec_like_with_escape( &pattern.as_str(), &text.as_str(), escape, ) as i64) } (OwnedValue::Text(pattern), OwnedValue::Text(text)) => { let cache = if *constant_mask > 0 { Some(&mut state.regex_cache.like) } else { None }; OwnedValue::Integer(exec_like( cache, &pattern.as_str(), &text.as_str(), ) as i64) } _ => { unreachable!("Like on non-text registers"); } }; state.registers[*dest] = result; } ScalarFunc::Abs | ScalarFunc::Lower | ScalarFunc::Upper | ScalarFunc::Length | ScalarFunc::OctetLength | ScalarFunc::Typeof | ScalarFunc::Unicode | ScalarFunc::Quote | ScalarFunc::RandomBlob | ScalarFunc::Sign | ScalarFunc::Soundex | ScalarFunc::ZeroBlob => { let reg_value = state.registers[*start_reg].borrow_mut(); let result = match scalar_func { ScalarFunc::Sign => exec_sign(reg_value), ScalarFunc::Abs => Some(exec_abs(reg_value)?), ScalarFunc::Lower => exec_lower(reg_value), ScalarFunc::Upper => exec_upper(reg_value), ScalarFunc::Length => Some(exec_length(reg_value)), ScalarFunc::OctetLength => Some(exec_octet_length(reg_value)), ScalarFunc::Typeof => Some(exec_typeof(reg_value)), ScalarFunc::Unicode => Some(exec_unicode(reg_value)), ScalarFunc::Quote => Some(exec_quote(reg_value)), ScalarFunc::RandomBlob => Some(exec_randomblob(reg_value)), ScalarFunc::ZeroBlob => Some(exec_zeroblob(reg_value)), ScalarFunc::Soundex => Some(exec_soundex(reg_value)), _ => unreachable!(), }; state.registers[*dest] = result.unwrap_or(OwnedValue::Null); } ScalarFunc::Hex => { let reg_value = state.registers[*start_reg].borrow_mut(); let result = exec_hex(reg_value); state.registers[*dest] = result; } ScalarFunc::Unhex => { let reg_value = state.registers[*start_reg].clone(); let ignored_chars = state.registers.get(*start_reg + 1); let result = exec_unhex(®_value, ignored_chars); state.registers[*dest] = result; } ScalarFunc::Random => { state.registers[*dest] = exec_random(); } ScalarFunc::Trim => { let reg_value = state.registers[*start_reg].clone(); let pattern_value = if func.arg_count == 2 { state.registers.get(*start_reg + 1).cloned() } else { None }; let result = exec_trim(®_value, pattern_value); state.registers[*dest] = result; } ScalarFunc::LTrim => { let reg_value = state.registers[*start_reg].clone(); let pattern_value = if func.arg_count == 2 { state.registers.get(*start_reg + 1).cloned() } else { None }; let result = exec_ltrim(®_value, pattern_value); state.registers[*dest] = result; } ScalarFunc::RTrim => { let reg_value = state.registers[*start_reg].clone(); let pattern_value = if func.arg_count == 2 { state.registers.get(*start_reg + 1).cloned() } else { None }; let result = exec_rtrim(®_value, pattern_value); state.registers[*dest] = result; } ScalarFunc::Round => { let reg_value = state.registers[*start_reg].clone(); assert!(arg_count == 1 || arg_count == 2); let precision_value = if arg_count > 1 { Some(state.registers[*start_reg + 1].clone()) } else { None }; let result = exec_round(®_value, precision_value); state.registers[*dest] = result; } ScalarFunc::Min => { let reg_values = state.registers [*start_reg..*start_reg + arg_count] .iter() .collect(); state.registers[*dest] = exec_min(reg_values); } ScalarFunc::Max => { let reg_values = state.registers [*start_reg..*start_reg + arg_count] .iter() .collect(); state.registers[*dest] = exec_max(reg_values); } ScalarFunc::Nullif => { let first_value = &state.registers[*start_reg]; let second_value = &state.registers[*start_reg + 1]; state.registers[*dest] = exec_nullif(first_value, second_value); } ScalarFunc::Substr | ScalarFunc::Substring => { let str_value = &state.registers[*start_reg]; let start_value = &state.registers[*start_reg + 1]; let length_value = if func.arg_count == 3 { Some(&state.registers[*start_reg + 2]) } else { None }; let result = exec_substring(str_value, start_value, length_value); state.registers[*dest] = result; } ScalarFunc::Date => { let result = exec_date(&state.registers[*start_reg..*start_reg + arg_count]); state.registers[*dest] = result; } ScalarFunc::Time => { let result = exec_time(&state.registers[*start_reg..*start_reg + arg_count]); state.registers[*dest] = result; } ScalarFunc::TotalChanges => { let res = &self.connection.upgrade().unwrap().total_changes; let total_changes = res.get(); state.registers[*dest] = OwnedValue::Integer(total_changes); } ScalarFunc::DateTime => { let result = exec_datetime_full( &state.registers[*start_reg..*start_reg + arg_count], ); state.registers[*dest] = result; } ScalarFunc::JulianDay => { if *start_reg == 0 { let julianday: String = exec_julianday(&OwnedValue::build_text("now"))?; state.registers[*dest] = OwnedValue::build_text(&julianday); } else { let datetime_value = &state.registers[*start_reg]; let julianday = exec_julianday(datetime_value); match julianday { Ok(time) => { state.registers[*dest] = OwnedValue::build_text(&time) } Err(e) => { return Err(LimboError::ParseError(format!( "Error encountered while parsing datetime value: {}", e ))); } } } } ScalarFunc::UnixEpoch => { if *start_reg == 0 { let unixepoch: String = exec_unixepoch(&OwnedValue::build_text("now"))?; state.registers[*dest] = OwnedValue::build_text(&unixepoch); } else { let datetime_value = &state.registers[*start_reg]; let unixepoch = exec_unixepoch(datetime_value); match unixepoch { Ok(time) => { state.registers[*dest] = OwnedValue::build_text(&time) } Err(e) => { return Err(LimboError::ParseError(format!( "Error encountered while parsing datetime value: {}", e ))); } } } } ScalarFunc::SqliteVersion => { let version_integer: i64 = DATABASE_VERSION.get().unwrap().parse()?; let version = execute_sqlite_version(version_integer); state.registers[*dest] = OwnedValue::build_text(&version); } ScalarFunc::SqliteSourceId => { let src_id = format!( "{} {}", info::build::BUILT_TIME_SQLITE, info::build::GIT_COMMIT_HASH.unwrap_or("unknown") ); state.registers[*dest] = OwnedValue::build_text(&src_id); } ScalarFunc::Replace => { assert_eq!(arg_count, 3); let source = &state.registers[*start_reg]; let pattern = &state.registers[*start_reg + 1]; let replacement = &state.registers[*start_reg + 2]; state.registers[*dest] = exec_replace(source, pattern, replacement); } #[cfg(not(target_family = "wasm"))] ScalarFunc::LoadExtension => { let extension = &state.registers[*start_reg]; let ext = resolve_ext_path(&extension.to_string())?; if let Some(conn) = self.connection.upgrade() { conn.load_extension(ext)?; } } ScalarFunc::StrfTime => { let result = exec_strftime( &state.registers[*start_reg..*start_reg + arg_count], ); state.registers[*dest] = result; } ScalarFunc::Printf => { let result = exec_printf( &state.registers[*start_reg..*start_reg + arg_count], )?; state.registers[*dest] = result; } }, crate::function::Func::Vector(vector_func) => match vector_func { VectorFunc::Vector => { let result = vector32(&state.registers[*start_reg..*start_reg + arg_count])?; state.registers[*dest] = result; } VectorFunc::Vector32 => { let result = vector32(&state.registers[*start_reg..*start_reg + arg_count])?; state.registers[*dest] = result; } VectorFunc::Vector64 => { let result = vector64(&state.registers[*start_reg..*start_reg + arg_count])?; state.registers[*dest] = result; } VectorFunc::VectorExtract => { let result = vector_extract( &state.registers[*start_reg..*start_reg + arg_count], )?; state.registers[*dest] = result; } VectorFunc::VectorDistanceCos => { let result = vector_distance_cos( &state.registers[*start_reg..*start_reg + arg_count], )?; state.registers[*dest] = result; } }, crate::function::Func::External(f) => match f.func { ExtFunc::Scalar(f) => { call_external_function! {f, *dest, state, arg_count, *start_reg }; } _ => unreachable!("aggregate called in scalar context"), }, 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") } } state.pc += 1; } Insn::InitCoroutine { yield_reg, jump_on_definition, start_offset, } => { assert!(jump_on_definition.is_offset()); let start_offset = start_offset.to_offset_int(); state.registers[*yield_reg] = OwnedValue::Integer(start_offset as i64); state.ended_coroutine.unset(*yield_reg); let jump_on_definition = jump_on_definition.to_offset_int(); state.pc = if jump_on_definition == 0 { state.pc + 1 } else { jump_on_definition }; } Insn::EndCoroutine { yield_reg } => { if let OwnedValue::Integer(pc) = state.registers[*yield_reg] { state.ended_coroutine.set(*yield_reg); let pc: u32 = pc .try_into() .unwrap_or_else(|_| panic!("EndCoroutine: pc overflow: {}", pc)); state.pc = pc - 1; // yield jump is always next to yield. Here we subtract 1 to go back to yield instruction } else { unreachable!(); } } Insn::Yield { yield_reg, end_offset, } => { if let OwnedValue::Integer(pc) = state.registers[*yield_reg] { if state.ended_coroutine.get(*yield_reg) { state.pc = end_offset.to_offset_int(); } else { let pc: u32 = pc .try_into() .unwrap_or_else(|_| panic!("Yield: pc overflow: {}", pc)); // swap the program counter with the value in the yield register // this is the mechanism that allows jumping back and forth between the coroutine and the caller (state.pc, state.registers[*yield_reg]) = (pc, OwnedValue::Integer((state.pc + 1) as i64)); } } else { unreachable!( "yield_reg {} contains non-integer value: {:?}", *yield_reg, state.registers[*yield_reg] ); } } Insn::InsertAsync { cursor, key_reg, record_reg, flag: _, } => { let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_table_mut(&mut cursors, *cursor); let record = match &state.registers[*record_reg] { OwnedValue::Record(r) => r, _ => unreachable!("Not a record! Cannot insert a non record value."), }; let key = &state.registers[*key_reg]; return_if_io!(cursor.insert(key, record, true)); state.pc += 1; } Insn::InsertAwait { cursor_id } => { let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_table_mut(&mut cursors, *cursor_id); cursor.wait_for_completion()?; // Only update last_insert_rowid for regular table inserts, not schema modifications if cursor.root_page() != 1 { if let Some(rowid) = cursor.rowid()? { if let Some(conn) = self.connection.upgrade() { conn.update_last_rowid(rowid); } let prev_changes = self.n_change.get(); self.n_change.set(prev_changes + 1); } } state.pc += 1; } Insn::DeleteAsync { cursor_id } => { let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_table_mut(&mut cursors, *cursor_id); return_if_io!(cursor.delete()); state.pc += 1; } Insn::DeleteAwait { cursor_id } => { let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_table_mut(&mut cursors, *cursor_id); cursor.wait_for_completion()?; let prev_changes = self.n_change.get(); self.n_change.set(prev_changes + 1); state.pc += 1; } Insn::NewRowid { cursor, rowid_reg, .. } => { let mut cursors = state.cursors.borrow_mut(); let cursor = get_cursor_as_table_mut(&mut cursors, *cursor); // TODO: make io handle rng let rowid = return_if_io!(get_new_rowid(cursor, thread_rng())); state.registers[*rowid_reg] = OwnedValue::Integer(rowid); state.pc += 1; } Insn::MustBeInt { reg } => { match &state.registers[*reg] { OwnedValue::Integer(_) => {} OwnedValue::Float(f) => match cast_real_to_integer(*f) { Ok(i) => state.registers[*reg] = OwnedValue::Integer(i), Err(_) => crate::bail_parse_error!( "MustBeInt: the value in register cannot be cast to integer" ), }, OwnedValue::Text(text) => { match checked_cast_text_to_numeric(&text.as_str()) { Ok(OwnedValue::Integer(i)) => { state.registers[*reg] = OwnedValue::Integer(i) } _ => crate::bail_parse_error!( "MustBeInt: the value in register cannot be cast to integer" ), } } _ => { crate::bail_parse_error!( "MustBeInt: the value in register cannot be cast to integer" ); } }; state.pc += 1; } Insn::SoftNull { reg } => { state.registers[*reg] = OwnedValue::Null; state.pc += 1; } Insn::NotExists { cursor, rowid_reg, target_pc, } => { let mut cursors = state.cursors.borrow_mut(); let cursor = must_be_btree_cursor!(*cursor, self.cursor_ref, cursors, "NotExists"); let exists = return_if_io!(cursor.exists(&state.registers[*rowid_reg])); if exists { state.pc += 1; } else { state.pc = target_pc.to_offset_int(); } } Insn::OffsetLimit { limit_reg, combined_reg, offset_reg, } => { let limit_val = match state.registers[*limit_reg] { OwnedValue::Integer(val) => val, _ => { return Err(LimboError::InternalError( "OffsetLimit: the value in limit_reg is not an integer".into(), )); } }; let offset_val = match state.registers[*offset_reg] { OwnedValue::Integer(val) if val < 0 => 0, OwnedValue::Integer(val) if val >= 0 => val, _ => { return Err(LimboError::InternalError( "OffsetLimit: the value in offset_reg is not an integer".into(), )); } }; let offset_limit_sum = limit_val.overflowing_add(offset_val); if limit_val <= 0 || offset_limit_sum.1 { state.registers[*combined_reg] = OwnedValue::Integer(-1); } else { state.registers[*combined_reg] = OwnedValue::Integer(offset_limit_sum.0); } state.pc += 1; } // this cursor may be reused for next insert // Update: tablemoveto is used to travers on not exists, on insert depending on flags if nonseek it traverses again. // If not there might be some optimizations obviously. Insn::OpenWriteAsync { cursor_id, root_page, } => { let (_, cursor_type) = self.cursor_ref.get(*cursor_id).unwrap(); let mut cursors = state.cursors.borrow_mut(); let is_index = cursor_type.is_index(); let cursor = BTreeCursor::new(pager.clone(), *root_page); if is_index { cursors .get_mut(*cursor_id) .unwrap() .replace(Cursor::new_index(cursor)); } else { cursors .get_mut(*cursor_id) .unwrap() .replace(Cursor::new_table(cursor)); } state.pc += 1; } Insn::OpenWriteAwait {} => { state.pc += 1; } Insn::Copy { src_reg, dst_reg, amount, } => { for i in 0..=*amount { state.registers[*dst_reg + i] = state.registers[*src_reg + i].clone(); } state.pc += 1; } Insn::CreateBtree { db, root, flags } => { if *db > 0 { // TODO: implement temp databases todo!("temp databases not implemented yet"); } let mut cursor = Box::new(BTreeCursor::new(pager.clone(), 0)); let root_page = cursor.btree_create(*flags); state.registers[*root] = OwnedValue::Integer(root_page as i64); state.pc += 1; } Insn::Destroy { root, former_root_reg: _, is_temp, } => { if *is_temp == 1 { todo!("temp databases not implemented yet."); } let mut cursor = Box::new(BTreeCursor::new(pager.clone(), *root)); cursor.btree_destroy()?; state.pc += 1; } Insn::DropTable { db, _p2, _p3, table_name, } => { if *db > 0 { todo!("temp databases not implemented yet"); } if let Some(conn) = self.connection.upgrade() { let mut schema = RefCell::borrow_mut(&conn.schema); schema.remove_indices_for_table(table_name); schema.remove_table(table_name); } state.pc += 1; } Insn::Close { cursor_id } => { let mut cursors = state.cursors.borrow_mut(); cursors.get_mut(*cursor_id).unwrap().take(); state.pc += 1; } Insn::IsNull { reg, target_pc } => { if matches!(state.registers[*reg], OwnedValue::Null) { state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } Insn::PageCount { db, dest } => { if *db > 0 { // TODO: implement temp databases todo!("temp databases not implemented yet"); } // SQLite returns "0" on an empty database, and 2 on the first insertion, // so we'll mimic that behavior. let mut pages = pager.db_header.borrow().database_size.into(); if pages == 1 { pages = 0; } state.registers[*dest] = OwnedValue::Integer(pages); state.pc += 1; } Insn::ParseSchema { db: _, where_clause, } => { let conn = self.connection.upgrade(); let conn = conn.as_ref().unwrap(); let stmt = conn.prepare(format!( "SELECT * FROM sqlite_schema WHERE {}", where_clause ))?; let mut schema = RefCell::borrow_mut(&conn.schema); // TODO: This function below is synchronous, make it not async parse_schema_rows(Some(stmt), &mut schema, conn.pager.io.clone())?; state.pc += 1; } Insn::ReadCookie { db, dest, cookie } => { if *db > 0 { // TODO: implement temp databases todo!("temp databases not implemented yet"); } let cookie_value = match cookie { Cookie::UserVersion => pager.db_header.borrow().user_version.into(), cookie => todo!("{cookie:?} is not yet implement for ReadCookie"), }; state.registers[*dest] = OwnedValue::Integer(cookie_value); state.pc += 1; } Insn::ShiftRight { lhs, rhs, dest } => { state.registers[*dest] = exec_shift_right(&state.registers[*lhs], &state.registers[*rhs]); state.pc += 1; } Insn::ShiftLeft { lhs, rhs, dest } => { state.registers[*dest] = exec_shift_left(&state.registers[*lhs], &state.registers[*rhs]); state.pc += 1; } Insn::Variable { index, dest } => { state.registers[*dest] = state .get_parameter(*index) .ok_or(LimboError::Unbound(*index))? .clone(); state.pc += 1; } Insn::ZeroOrNull { rg1, rg2, dest } => { if state.registers[*rg1] == OwnedValue::Null || state.registers[*rg2] == OwnedValue::Null { state.registers[*dest] = OwnedValue::Null } else { state.registers[*dest] = OwnedValue::Integer(0); } state.pc += 1; } Insn::Not { reg, dest } => { state.registers[*dest] = exec_boolean_not(&state.registers[*reg]); state.pc += 1; } Insn::Concat { lhs, rhs, dest } => { state.registers[*dest] = exec_concat(&state.registers[*lhs], &state.registers[*rhs]); state.pc += 1; } Insn::And { lhs, rhs, dest } => { state.registers[*dest] = exec_and(&state.registers[*lhs], &state.registers[*rhs]); state.pc += 1; } Insn::Or { lhs, rhs, dest } => { state.registers[*dest] = exec_or(&state.registers[*lhs], &state.registers[*rhs]); state.pc += 1; } Insn::Noop => { // Do nothing // Advance the program counter for the next opcode state.pc += 1 } } } } fn halt(&self, pager: Rc) -> Result { let connection = self .connection .upgrade() .expect("only weak ref to connection?"); let auto_commit = *connection.auto_commit.borrow(); tracing::trace!("Halt auto_commit {}", auto_commit); return if auto_commit { let current_state = connection.transaction_state.borrow().clone(); if current_state == TransactionState::Read { pager.end_read_tx()?; return Ok(StepResult::Done); } match pager.end_tx() { Ok(crate::storage::wal::CheckpointStatus::IO) => Ok(StepResult::IO), Ok(crate::storage::wal::CheckpointStatus::Done(_)) => { if self.change_cnt_on { if let Some(conn) = self.connection.upgrade() { conn.set_changes(self.n_change.get()); } } Ok(StepResult::Done) } Err(e) => Err(e), } } else { if self.change_cnt_on { if let Some(conn) = self.connection.upgrade() { conn.set_changes(self.n_change.get()); } } return Ok(StepResult::Done); }; } } fn get_new_rowid(cursor: &mut BTreeCursor, mut rng: R) -> Result> { match cursor.seek_to_last()? { CursorResult::Ok(()) => {} CursorResult::IO => return Ok(CursorResult::IO), } let mut rowid = cursor .rowid()? .unwrap_or(0) // if BTree is empty - use 0 as initial value for rowid .checked_add(1) // add 1 but be careful with overflows .unwrap_or(u64::MAX); // in case of overflow - use u64::MAX if rowid > i64::MAX.try_into().unwrap() { let distribution = Uniform::from(1..=i64::MAX); let max_attempts = 100; for count in 0..max_attempts { rowid = distribution.sample(&mut rng).try_into().unwrap(); match cursor.seek(SeekKey::TableRowId(rowid), SeekOp::EQ)? { CursorResult::Ok(false) => break, // Found a non-existing rowid CursorResult::Ok(true) => { if count == max_attempts - 1 { return Err(LimboError::InternalError( "Failed to generate a new rowid".to_string(), )); } else { continue; // Try next random rowid } } CursorResult::IO => return Ok(CursorResult::IO), } } } Ok(CursorResult::Ok(rowid.try_into().unwrap())) } fn make_owned_record(registers: &[OwnedValue], start_reg: &usize, count: &usize) -> Record { let mut values = Vec::with_capacity(*count); for r in registers.iter().skip(*start_reg).take(*count) { values.push(r.clone()) } Record::new(values) } fn trace_insn(program: &Program, addr: InsnReference, insn: &Insn) { if !tracing::enabled!(tracing::Level::TRACE) { return; } tracing::trace!( "{}", explain::insn_to_str( program, addr, insn, String::new(), program .comments .as_ref() .and_then(|comments| comments.get(&{ addr }).copied()) ) ); } fn print_insn(program: &Program, addr: InsnReference, insn: &Insn, indent: String) { let s = explain::insn_to_str( program, addr, insn, indent, program .comments .as_ref() .and_then(|comments| comments.get(&{ addr }).copied()), ); println!("{}", s); } fn get_indent_count(indent_count: usize, curr_insn: &Insn, prev_insn: Option<&Insn>) -> usize { let indent_count = if let Some(insn) = prev_insn { match insn { Insn::RewindAwait { .. } | Insn::LastAwait { .. } | Insn::SorterSort { .. } | Insn::SeekGE { .. } | Insn::SeekGT { .. } => indent_count + 1, _ => indent_count, } } else { indent_count }; match curr_insn { Insn::NextAsync { .. } | Insn::SorterNext { .. } | Insn::PrevAsync { .. } => { indent_count - 1 } _ => indent_count, } } fn exec_lower(reg: &OwnedValue) -> Option { match reg { OwnedValue::Text(t) => Some(OwnedValue::build_text(&t.as_str().to_lowercase())), t => Some(t.to_owned()), } } fn exec_length(reg: &OwnedValue) -> OwnedValue { match reg { OwnedValue::Text(_) | OwnedValue::Integer(_) | OwnedValue::Float(_) => { OwnedValue::Integer(reg.to_string().chars().count() as i64) } OwnedValue::Blob(blob) => OwnedValue::Integer(blob.len() as i64), OwnedValue::Agg(aggctx) => exec_length(aggctx.final_value()), _ => reg.to_owned(), } } fn exec_octet_length(reg: &OwnedValue) -> OwnedValue { match reg { OwnedValue::Text(_) | OwnedValue::Integer(_) | OwnedValue::Float(_) => { OwnedValue::Integer(reg.to_string().into_bytes().len() as i64) } OwnedValue::Blob(blob) => OwnedValue::Integer(blob.len() as i64), OwnedValue::Agg(aggctx) => exec_octet_length(aggctx.final_value()), _ => reg.to_owned(), } } fn exec_upper(reg: &OwnedValue) -> Option { match reg { OwnedValue::Text(t) => Some(OwnedValue::build_text(&t.as_str().to_uppercase())), t => Some(t.to_owned()), } } fn exec_concat_strings(registers: &[OwnedValue]) -> OwnedValue { let mut result = String::new(); for reg in registers { match reg { OwnedValue::Text(text) => result.push_str(text.as_str()), OwnedValue::Integer(i) => result.push_str(&i.to_string()), OwnedValue::Float(f) => result.push_str(&f.to_string()), OwnedValue::Agg(aggctx) => result.push_str(&aggctx.final_value().to_string()), OwnedValue::Null => continue, OwnedValue::Blob(_) => todo!("TODO concat blob"), OwnedValue::Record(_) => unreachable!(), } } OwnedValue::build_text(&result) } fn exec_concat_ws(registers: &[OwnedValue]) -> OwnedValue { if registers.is_empty() { return OwnedValue::Null; } let separator = match ®isters[0] { OwnedValue::Text(text) => text.as_str().to_string(), OwnedValue::Integer(i) => i.to_string(), OwnedValue::Float(f) => f.to_string(), _ => return OwnedValue::Null, }; let mut result = String::new(); for (i, reg) in registers.iter().enumerate().skip(1) { if i > 1 { result.push_str(&separator); } match reg { OwnedValue::Text(text) => result.push_str(text.as_str()), OwnedValue::Integer(i) => result.push_str(&i.to_string()), OwnedValue::Float(f) => result.push_str(&f.to_string()), _ => continue, } } OwnedValue::build_text(&result) } fn exec_sign(reg: &OwnedValue) -> Option { let num = match reg { OwnedValue::Integer(i) => *i as f64, OwnedValue::Float(f) => *f, OwnedValue::Text(s) => { if let Ok(i) = s.as_str().parse::() { i as f64 } else if let Ok(f) = s.as_str().parse::() { f } else { return Some(OwnedValue::Null); } } OwnedValue::Blob(b) => match std::str::from_utf8(b) { Ok(s) => { if let Ok(i) = s.parse::() { i as f64 } else if let Ok(f) = s.parse::() { f } else { return Some(OwnedValue::Null); } } Err(_) => return Some(OwnedValue::Null), }, _ => return Some(OwnedValue::Null), }; let sign = if num > 0.0 { 1 } else if num < 0.0 { -1 } else { 0 }; Some(OwnedValue::Integer(sign)) } /// Generates the Soundex code for a given word pub fn exec_soundex(reg: &OwnedValue) -> OwnedValue { let s = match reg { OwnedValue::Null => return OwnedValue::build_text("?000"), OwnedValue::Text(s) => { // return ?000 if non ASCII alphabet character is found if !s.as_str().chars().all(|c| c.is_ascii_alphabetic()) { return OwnedValue::build_text("?000"); } s.clone() } _ => return OwnedValue::build_text("?000"), // For unsupported types, return NULL }; // Remove numbers and spaces let word: String = s .as_str() .chars() .filter(|c| !c.is_ascii_digit()) .collect::() .replace(" ", ""); if word.is_empty() { return OwnedValue::build_text("0000"); } let soundex_code = |c| match c { 'b' | 'f' | 'p' | 'v' => Some('1'), 'c' | 'g' | 'j' | 'k' | 'q' | 's' | 'x' | 'z' => Some('2'), 'd' | 't' => Some('3'), 'l' => Some('4'), 'm' | 'n' => Some('5'), 'r' => Some('6'), _ => None, }; // Convert the word to lowercase for consistent lookups let word = word.to_lowercase(); let first_letter = word.chars().next().unwrap(); // Remove all occurrences of 'h' and 'w' except the first letter let code: String = word .chars() .skip(1) .filter(|&ch| ch != 'h' && ch != 'w') .fold(first_letter.to_string(), |mut acc, ch| { acc.push(ch); acc }); // Replace consonants with digits based on Soundex mapping let tmp: String = code .chars() .map(|ch| match soundex_code(ch) { Some(code) => code.to_string(), None => ch.to_string(), }) .collect(); // Remove adjacent same digits let tmp = tmp.chars().fold(String::new(), |mut acc, ch| { if !acc.ends_with(ch) { acc.push(ch); } acc }); // Remove all occurrences of a, e, i, o, u, y except the first letter let mut result = tmp .chars() .enumerate() .filter(|(i, ch)| *i == 0 || !matches!(ch, 'a' | 'e' | 'i' | 'o' | 'u' | 'y')) .map(|(_, ch)| ch) .collect::(); // If the first symbol is a digit, replace it with the saved first letter if let Some(first_digit) = result.chars().next() { if first_digit.is_ascii_digit() { result.replace_range(0..1, &first_letter.to_string()); } } // Append zeros if the result contains less than 4 characters while result.len() < 4 { result.push('0'); } // Retain the first 4 characters and convert to uppercase result.truncate(4); OwnedValue::build_text(&result.to_uppercase()) } fn exec_abs(reg: &OwnedValue) -> Result { match reg { OwnedValue::Integer(x) => { match i64::checked_abs(*x) { Some(y) => Ok(OwnedValue::Integer(y)), // Special case: if we do the abs of "-9223372036854775808", it causes overflow. // return IntegerOverflow error None => Err(LimboError::IntegerOverflow), } } OwnedValue::Float(x) => { if x < &0.0 { Ok(OwnedValue::Float(-x)) } else { Ok(OwnedValue::Float(*x)) } } OwnedValue::Null => Ok(OwnedValue::Null), _ => Ok(OwnedValue::Float(0.0)), } } fn exec_random() -> OwnedValue { let mut buf = [0u8; 8]; getrandom::getrandom(&mut buf).unwrap(); let random_number = i64::from_ne_bytes(buf); OwnedValue::Integer(random_number) } fn exec_randomblob(reg: &OwnedValue) -> OwnedValue { let length = match reg { OwnedValue::Integer(i) => *i, OwnedValue::Float(f) => *f as i64, OwnedValue::Text(t) => t.as_str().parse().unwrap_or(1), _ => 1, } .max(1) as usize; let mut blob: Vec = vec![0; length]; getrandom::getrandom(&mut blob).expect("Failed to generate random blob"); OwnedValue::Blob(Rc::new(blob)) } fn exec_quote(value: &OwnedValue) -> OwnedValue { match value { OwnedValue::Null => OwnedValue::build_text(&OwnedValue::Null.to_string()), OwnedValue::Integer(_) | OwnedValue::Float(_) => value.to_owned(), OwnedValue::Blob(_) => todo!(), OwnedValue::Text(s) => { let mut quoted = String::with_capacity(s.as_str().len() + 2); quoted.push('\''); for c in s.as_str().chars() { if c == '\0' { break; } else if c == '\'' { quoted.push('\''); quoted.push(c); } else { quoted.push(c); } } quoted.push('\''); OwnedValue::build_text("ed) } _ => OwnedValue::Null, // For unsupported types, return NULL } } fn exec_char(values: Vec) -> OwnedValue { let result: String = values .iter() .filter_map(|x| { if let OwnedValue::Integer(i) = x { Some(*i as u8 as char) } else { None } }) .collect(); OwnedValue::build_text(&result) } fn construct_like_regex(pattern: &str) -> Regex { let mut regex_pattern = String::with_capacity(pattern.len() * 2); regex_pattern.push('^'); for c in pattern.chars() { match c { '\\' => regex_pattern.push_str("\\\\"), '%' => regex_pattern.push_str(".*"), '_' => regex_pattern.push('.'), ch => { if regex_syntax::is_meta_character(c) { regex_pattern.push('\\'); } regex_pattern.push(ch); } } } regex_pattern.push('$'); RegexBuilder::new(®ex_pattern) .case_insensitive(true) .dot_matches_new_line(true) .build() .unwrap() } // Implements LIKE pattern matching. Caches the constructed regex if a cache is provided fn exec_like(regex_cache: Option<&mut HashMap>, pattern: &str, text: &str) -> bool { if let Some(cache) = regex_cache { match cache.get(pattern) { Some(re) => re.is_match(text), None => { let re = construct_like_regex(pattern); let res = re.is_match(text); cache.insert(pattern.to_string(), re); res } } } else { let re = construct_like_regex(pattern); re.is_match(text) } } fn exec_min(regs: Vec<&OwnedValue>) -> OwnedValue { regs.iter() .min() .map(|&v| v.to_owned()) .unwrap_or(OwnedValue::Null) } fn exec_max(regs: Vec<&OwnedValue>) -> OwnedValue { regs.iter() .max() .map(|&v| v.to_owned()) .unwrap_or(OwnedValue::Null) } fn exec_nullif(first_value: &OwnedValue, second_value: &OwnedValue) -> OwnedValue { if first_value != second_value { first_value.clone() } else { OwnedValue::Null } } fn exec_substring( str_value: &OwnedValue, start_value: &OwnedValue, length_value: Option<&OwnedValue>, ) -> OwnedValue { if let (OwnedValue::Text(str), OwnedValue::Integer(start)) = (str_value, start_value) { let str_len = str.as_str().len() as i64; // The left-most character of X is number 1. // If Y is negative then the first character of the substring is found by counting from the right rather than the left. let first_position = if *start < 0 { str_len.saturating_sub((*start).abs()) } else { *start - 1 }; // If Z is negative then the abs(Z) characters preceding the Y-th character are returned. let last_position = match length_value { Some(OwnedValue::Integer(length)) => first_position + *length, _ => str_len, }; let (start, end) = if first_position <= last_position { (first_position, last_position) } else { (last_position, first_position) }; OwnedValue::build_text( &str.as_str()[start.clamp(-0, str_len) as usize..end.clamp(0, str_len) as usize], ) } else { OwnedValue::Null } } fn exec_instr(reg: &OwnedValue, pattern: &OwnedValue) -> OwnedValue { if reg == &OwnedValue::Null || pattern == &OwnedValue::Null { return OwnedValue::Null; } if let (OwnedValue::Blob(reg), OwnedValue::Blob(pattern)) = (reg, pattern) { let result = reg .windows(pattern.len()) .position(|window| window == **pattern) .map_or(0, |i| i + 1); return OwnedValue::Integer(result as i64); } let reg_str; let reg = match reg { OwnedValue::Text(s) => s.as_str(), _ => { reg_str = reg.to_string(); reg_str.as_str() } }; let pattern_str; let pattern = match pattern { OwnedValue::Text(s) => s.as_str(), _ => { pattern_str = pattern.to_string(); pattern_str.as_str() } }; match reg.find(pattern) { Some(position) => OwnedValue::Integer(position as i64 + 1), None => OwnedValue::Integer(0), } } fn exec_typeof(reg: &OwnedValue) -> OwnedValue { match reg { OwnedValue::Null => OwnedValue::build_text("null"), OwnedValue::Integer(_) => OwnedValue::build_text("integer"), OwnedValue::Float(_) => OwnedValue::build_text("real"), OwnedValue::Text(_) => OwnedValue::build_text("text"), OwnedValue::Blob(_) => OwnedValue::build_text("blob"), OwnedValue::Agg(ctx) => exec_typeof(ctx.final_value()), OwnedValue::Record(_) => unimplemented!(), } } fn exec_hex(reg: &OwnedValue) -> OwnedValue { match reg { OwnedValue::Text(_) | OwnedValue::Integer(_) | OwnedValue::Float(_) | OwnedValue::Blob(_) => { let text = reg.to_string(); OwnedValue::build_text(&hex::encode_upper(text)) } _ => OwnedValue::Null, } } fn exec_unhex(reg: &OwnedValue, ignored_chars: Option<&OwnedValue>) -> OwnedValue { match reg { OwnedValue::Null => OwnedValue::Null, _ => match ignored_chars { None => match hex::decode(reg.to_string()) { Ok(bytes) => OwnedValue::Blob(Rc::new(bytes)), Err(_) => OwnedValue::Null, }, Some(ignore) => match ignore { OwnedValue::Text(_) => { let pat = ignore.to_string(); let trimmed = reg .to_string() .trim_start_matches(|x| pat.contains(x)) .trim_end_matches(|x| pat.contains(x)) .to_string(); match hex::decode(trimmed) { Ok(bytes) => OwnedValue::Blob(Rc::new(bytes)), Err(_) => OwnedValue::Null, } } _ => OwnedValue::Null, }, }, } } fn exec_unicode(reg: &OwnedValue) -> OwnedValue { match reg { OwnedValue::Text(_) | OwnedValue::Integer(_) | OwnedValue::Float(_) | OwnedValue::Blob(_) => { let text = reg.to_string(); if let Some(first_char) = text.chars().next() { OwnedValue::Integer(first_char as u32 as i64) } else { OwnedValue::Null } } _ => OwnedValue::Null, } } fn _to_float(reg: &OwnedValue) -> f64 { match reg { OwnedValue::Text(x) => x.as_str().parse().unwrap_or(0.0), OwnedValue::Integer(x) => *x as f64, OwnedValue::Float(x) => *x, _ => 0.0, } } fn exec_round(reg: &OwnedValue, precision: Option) -> OwnedValue { let precision = match precision { Some(OwnedValue::Text(x)) => x.as_str().parse().unwrap_or(0.0), Some(OwnedValue::Integer(x)) => x as f64, Some(OwnedValue::Float(x)) => x, Some(OwnedValue::Null) => return OwnedValue::Null, _ => 0.0, }; let reg = match reg { OwnedValue::Agg(ctx) => _to_float(ctx.final_value()), _ => _to_float(reg), }; let precision = if precision < 1.0 { 0.0 } else { precision }; let multiplier = 10f64.powi(precision as i32); OwnedValue::Float(((reg * multiplier).round()) / multiplier) } // Implements TRIM pattern matching. fn exec_trim(reg: &OwnedValue, pattern: Option) -> OwnedValue { match (reg, pattern) { (reg, Some(pattern)) => match reg { OwnedValue::Text(_) | OwnedValue::Integer(_) | OwnedValue::Float(_) => { let pattern_chars: Vec = pattern.to_string().chars().collect(); OwnedValue::build_text(reg.to_string().trim_matches(&pattern_chars[..])) } _ => reg.to_owned(), }, (OwnedValue::Text(t), None) => OwnedValue::build_text(t.as_str().trim()), (reg, _) => reg.to_owned(), } } // Implements LTRIM pattern matching. fn exec_ltrim(reg: &OwnedValue, pattern: Option) -> OwnedValue { match (reg, pattern) { (reg, Some(pattern)) => match reg { OwnedValue::Text(_) | OwnedValue::Integer(_) | OwnedValue::Float(_) => { let pattern_chars: Vec = pattern.to_string().chars().collect(); OwnedValue::build_text(reg.to_string().trim_start_matches(&pattern_chars[..])) } _ => reg.to_owned(), }, (OwnedValue::Text(t), None) => OwnedValue::build_text(t.as_str().trim_start()), (reg, _) => reg.to_owned(), } } // Implements RTRIM pattern matching. fn exec_rtrim(reg: &OwnedValue, pattern: Option) -> OwnedValue { match (reg, pattern) { (reg, Some(pattern)) => match reg { OwnedValue::Text(_) | OwnedValue::Integer(_) | OwnedValue::Float(_) => { let pattern_chars: Vec = pattern.to_string().chars().collect(); OwnedValue::build_text(reg.to_string().trim_end_matches(&pattern_chars[..])) } _ => reg.to_owned(), }, (OwnedValue::Text(t), None) => OwnedValue::build_text(t.as_str().trim_end()), (reg, _) => reg.to_owned(), } } fn exec_zeroblob(req: &OwnedValue) -> OwnedValue { let length: i64 = match req { OwnedValue::Integer(i) => *i, OwnedValue::Float(f) => *f as i64, OwnedValue::Text(s) => s.as_str().parse().unwrap_or(0), _ => 0, }; OwnedValue::Blob(Rc::new(vec![0; length.max(0) as usize])) } // exec_if returns whether you should jump fn exec_if(reg: &OwnedValue, jump_if_null: bool, not: bool) -> bool { match reg { OwnedValue::Integer(0) | OwnedValue::Float(0.0) => not, OwnedValue::Integer(_) | OwnedValue::Float(_) => !not, OwnedValue::Null => jump_if_null, _ => false, } } fn exec_cast(value: &OwnedValue, datatype: &str) -> OwnedValue { if matches!(value, OwnedValue::Null) { return OwnedValue::Null; } match affinity(datatype) { // NONE Casting a value to a type-name with no affinity causes the value to be converted into a BLOB. Casting to a BLOB consists of first casting the value to TEXT in the encoding of the database connection, then interpreting the resulting byte sequence as a BLOB instead of as TEXT. // Historically called NONE, but it's the same as BLOB Affinity::Blob => { // Convert to TEXT first, then interpret as BLOB // TODO: handle encoding let text = value.to_string(); OwnedValue::Blob(Rc::new(text.into_bytes())) } // TEXT To cast a BLOB value to TEXT, the sequence of bytes that make up the BLOB is interpreted as text encoded using the database encoding. // Casting an INTEGER or REAL value into TEXT renders the value as if via sqlite3_snprintf() except that the resulting TEXT uses the encoding of the database connection. Affinity::Text => { // Convert everything to text representation // TODO: handle encoding and whatever sqlite3_snprintf does OwnedValue::build_text(&value.to_string()) } Affinity::Real => match value { OwnedValue::Blob(b) => { // Convert BLOB to TEXT first let text = String::from_utf8_lossy(b); cast_text_to_real(&text) } OwnedValue::Text(t) => cast_text_to_real(t.as_str()), OwnedValue::Integer(i) => OwnedValue::Float(*i as f64), OwnedValue::Float(f) => OwnedValue::Float(*f), _ => OwnedValue::Float(0.0), }, Affinity::Integer => match value { OwnedValue::Blob(b) => { // Convert BLOB to TEXT first let text = String::from_utf8_lossy(b); cast_text_to_integer(&text) } OwnedValue::Text(t) => cast_text_to_integer(t.as_str()), OwnedValue::Integer(i) => OwnedValue::Integer(*i), // A cast of a REAL value into an INTEGER results in the integer between the REAL value and zero // that is closest to the REAL value. If a REAL is greater than the greatest possible signed integer (+9223372036854775807) // 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.trunc() as i128; if i > i64::MAX as i128 { OwnedValue::Integer(i64::MAX) } else if i < i64::MIN as i128 { OwnedValue::Integer(i64::MIN) } else { OwnedValue::Integer(i as i64) } } _ => OwnedValue::Integer(0), }, Affinity::Numeric => match value { OwnedValue::Blob(b) => { let text = String::from_utf8_lossy(b); cast_text_to_numeric(&text) } OwnedValue::Text(t) => cast_text_to_numeric(t.as_str()), OwnedValue::Integer(i) => OwnedValue::Integer(*i), OwnedValue::Float(f) => OwnedValue::Float(*f), _ => value.clone(), // TODO probably wrong }, } } fn exec_replace(source: &OwnedValue, pattern: &OwnedValue, replacement: &OwnedValue) -> OwnedValue { // The replace(X,Y,Z) function returns a string formed by substituting string Z for every occurrence of // string Y in string X. The BINARY collating sequence is used for comparisons. If Y is an empty string // then return X unchanged. If Z is not initially a string, it is cast to a UTF-8 string prior to processing. // If any of the arguments is NULL, the result is NULL. if matches!(source, OwnedValue::Null) || matches!(pattern, OwnedValue::Null) || matches!(replacement, OwnedValue::Null) { return OwnedValue::Null; } let source = exec_cast(source, "TEXT"); let pattern = exec_cast(pattern, "TEXT"); let replacement = exec_cast(replacement, "TEXT"); // If any of the casts failed, panic as text casting is not expected to fail. match (&source, &pattern, &replacement) { (OwnedValue::Text(source), OwnedValue::Text(pattern), OwnedValue::Text(replacement)) => { if pattern.as_str().is_empty() { return OwnedValue::Text(source.clone()); } let result = source .as_str() .replace(pattern.as_str(), replacement.as_str()); OwnedValue::build_text(&result) } _ => unreachable!("text cast should never fail"), } } /// When casting a TEXT value to INTEGER, the longest possible prefix of the value that can be interpreted as an integer number /// is extracted from the TEXT value and the remainder ignored. Any leading spaces in the TEXT value when converting from TEXT to INTEGER are ignored. /// If there is no prefix that can be interpreted as an integer number, the result of the conversion is 0. /// If the prefix integer is greater than +9223372036854775807 then the result of the cast is exactly +9223372036854775807. /// Similarly, if the prefix integer is less than -9223372036854775808 then the result of the cast is exactly -9223372036854775808. /// When casting to INTEGER, if the text looks like a floating point value with an exponent, the exponent will be ignored /// because it is no part of the integer prefix. For example, "CAST('123e+5' AS INTEGER)" results in 123, not in 12300000. /// The CAST operator understands decimal integers only β€” conversion of hexadecimal integers stops at the "x" in the "0x" prefix of the hexadecimal integer string and thus result of the CAST is always zero. fn cast_text_to_integer(text: &str) -> OwnedValue { let text = text.trim(); if text.is_empty() { return OwnedValue::Integer(0); } if let Ok(i) = text.parse::() { return OwnedValue::Integer(i); } // Try to find longest valid prefix that parses as an integer // TODO: inefficient let mut end_index = text.len().saturating_sub(1) as isize; while end_index >= 0 { if let Ok(i) = text[..=end_index as usize].parse::() { return OwnedValue::Integer(i); } end_index -= 1; } OwnedValue::Integer(0) } /// When casting a TEXT value to REAL, the longest possible prefix of the value that can be interpreted /// as a real number is extracted from the TEXT value and the remainder ignored. Any leading spaces in /// the TEXT value are ignored when converging from TEXT to REAL. /// If there is no prefix that can be interpreted as a real number, the result of the conversion is 0.0. fn cast_text_to_real(text: &str) -> OwnedValue { let trimmed = text.trim_start(); if trimmed.is_empty() { return OwnedValue::Float(0.0); } if let Ok(num) = trimmed.parse::() { return OwnedValue::Float(num); } // Try to find longest valid prefix that parses as a float // TODO: inefficient let mut end_index = trimmed.len().saturating_sub(1) as isize; while end_index >= 0 { if let Ok(num) = trimmed[..=end_index as usize].parse::() { return OwnedValue::Float(num); } end_index -= 1; } OwnedValue::Float(0.0) } /// NUMERIC Casting a TEXT or BLOB value into NUMERIC yields either an INTEGER or a REAL result. /// If the input text looks like an integer (there is no decimal point nor exponent) and the value /// is small enough to fit in a 64-bit signed integer, then the result will be INTEGER. /// Input text that looks like floating point (there is a decimal point and/or an exponent) /// and the text describes a value that can be losslessly converted back and forth between IEEE 754 /// 64-bit float and a 51-bit signed integer, then the result is INTEGER. (In the previous sentence, /// a 51-bit integer is specified since that is one bit less than the length of the mantissa of an /// IEEE 754 64-bit float and thus provides a 1-bit of margin for the text-to-float conversion operation.) /// Any text input that describes a value outside the range of a 64-bit signed integer yields a REAL result. /// Casting a REAL or INTEGER value to NUMERIC is a no-op, even if a real value could be losslessly converted to an integer. fn checked_cast_text_to_numeric(text: &str) -> std::result::Result { if !text.contains('.') && !text.contains('e') && !text.contains('E') { // Looks like an integer if let Ok(i) = text.parse::() { return Ok(OwnedValue::Integer(i)); } } // Try as float if let Ok(f) = text.parse::() { return match cast_real_to_integer(f) { Ok(i) => Ok(OwnedValue::Integer(i)), Err(_) => Ok(OwnedValue::Float(f)), }; } Err(()) } // try casting to numeric if not possible return integer 0 fn cast_text_to_numeric(text: &str) -> OwnedValue { checked_cast_text_to_numeric(text).unwrap_or(OwnedValue::Integer(0)) } // Check if float can be losslessly converted to 51-bit integer fn cast_real_to_integer(float: f64) -> std::result::Result { let i = float as i64; if float == i as f64 && i.abs() < (1i64 << 51) { return Ok(i); } Err(()) } fn execute_sqlite_version(version_integer: i64) -> String { let major = version_integer / 1_000_000; let minor = (version_integer % 1_000_000) / 1_000; let release = version_integer % 1_000; format!("{}.{}.{}", major, minor, release) } fn to_f64(reg: &OwnedValue) -> Option { match reg { OwnedValue::Integer(i) => Some(*i as f64), OwnedValue::Float(f) => Some(*f), OwnedValue::Text(t) => t.as_str().parse::().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 { use crate::vdbe::exec_replace; use super::{ exec_abs, exec_char, exec_hex, exec_if, exec_instr, exec_length, exec_like, exec_lower, exec_ltrim, exec_max, exec_min, exec_nullif, exec_quote, exec_random, exec_randomblob, exec_round, exec_rtrim, exec_sign, exec_soundex, exec_substring, exec_trim, exec_typeof, exec_unhex, exec_unicode, exec_upper, exec_zeroblob, execute_sqlite_version, AggContext, Bitfield, OwnedValue, }; use std::{collections::HashMap, rc::Rc}; #[test] fn test_length() { let input_str = OwnedValue::build_text("bob"); let expected_len = OwnedValue::Integer(3); assert_eq!(exec_length(&input_str), expected_len); let input_integer = OwnedValue::Integer(123); let expected_len = OwnedValue::Integer(3); assert_eq!(exec_length(&input_integer), expected_len); let input_float = OwnedValue::Float(123.456); let expected_len = OwnedValue::Integer(7); assert_eq!(exec_length(&input_float), expected_len); let expected_blob = OwnedValue::Blob(Rc::new("example".as_bytes().to_vec())); let expected_len = OwnedValue::Integer(7); assert_eq!(exec_length(&expected_blob), expected_len); } #[test] fn test_quote() { let input = OwnedValue::build_text("abc\0edf"); let expected = OwnedValue::build_text("'abc'"); assert_eq!(exec_quote(&input), expected); let input = OwnedValue::Integer(123); let expected = OwnedValue::Integer(123); assert_eq!(exec_quote(&input), expected); let input = OwnedValue::build_text("hello''world"); let expected = OwnedValue::build_text("'hello''''world'"); assert_eq!(exec_quote(&input), expected); } #[test] fn test_typeof() { let input = OwnedValue::Null; let expected: OwnedValue = OwnedValue::build_text("null"); assert_eq!(exec_typeof(&input), expected); let input = OwnedValue::Integer(123); let expected: OwnedValue = OwnedValue::build_text("integer"); assert_eq!(exec_typeof(&input), expected); let input = OwnedValue::Float(123.456); let expected: OwnedValue = OwnedValue::build_text("real"); assert_eq!(exec_typeof(&input), expected); let input = OwnedValue::build_text("hello"); let expected: OwnedValue = OwnedValue::build_text("text"); assert_eq!(exec_typeof(&input), expected); let input = OwnedValue::Blob(Rc::new("limbo".as_bytes().to_vec())); let expected: OwnedValue = OwnedValue::build_text("blob"); assert_eq!(exec_typeof(&input), expected); let input = OwnedValue::Agg(Box::new(AggContext::Sum(OwnedValue::Integer(123)))); let expected = OwnedValue::build_text("integer"); assert_eq!(exec_typeof(&input), expected); } #[test] fn test_unicode() { assert_eq!( exec_unicode(&OwnedValue::build_text("a")), OwnedValue::Integer(97) ); assert_eq!( exec_unicode(&OwnedValue::build_text("😊")), OwnedValue::Integer(128522) ); assert_eq!(exec_unicode(&OwnedValue::build_text("")), OwnedValue::Null); assert_eq!( exec_unicode(&OwnedValue::Integer(23)), OwnedValue::Integer(50) ); assert_eq!( exec_unicode(&OwnedValue::Integer(0)), OwnedValue::Integer(48) ); assert_eq!( exec_unicode(&OwnedValue::Float(0.0)), OwnedValue::Integer(48) ); assert_eq!( exec_unicode(&OwnedValue::Float(23.45)), OwnedValue::Integer(50) ); assert_eq!(exec_unicode(&OwnedValue::Null), OwnedValue::Null); assert_eq!( exec_unicode(&OwnedValue::Blob(Rc::new("example".as_bytes().to_vec()))), OwnedValue::Integer(101) ); } #[test] fn test_min_max() { let input_int_vec = vec![&OwnedValue::Integer(-1), &OwnedValue::Integer(10)]; assert_eq!(exec_min(input_int_vec.clone()), OwnedValue::Integer(-1)); assert_eq!(exec_max(input_int_vec.clone()), OwnedValue::Integer(10)); let str1 = OwnedValue::build_text("A"); let str2 = OwnedValue::build_text("z"); let input_str_vec = vec![&str2, &str1]; assert_eq!(exec_min(input_str_vec.clone()), OwnedValue::build_text("A")); assert_eq!(exec_max(input_str_vec.clone()), OwnedValue::build_text("z")); let input_null_vec = vec![&OwnedValue::Null, &OwnedValue::Null]; assert_eq!(exec_min(input_null_vec.clone()), OwnedValue::Null); assert_eq!(exec_max(input_null_vec.clone()), OwnedValue::Null); let input_mixed_vec = vec![&OwnedValue::Integer(10), &str1]; assert_eq!(exec_min(input_mixed_vec.clone()), OwnedValue::Integer(10)); assert_eq!( exec_max(input_mixed_vec.clone()), OwnedValue::build_text("A") ); } #[test] fn test_trim() { let input_str = OwnedValue::build_text(" Bob and Alice "); let expected_str = OwnedValue::build_text("Bob and Alice"); assert_eq!(exec_trim(&input_str, None), expected_str); let input_str = OwnedValue::build_text(" Bob and Alice "); let pattern_str = OwnedValue::build_text("Bob and"); let expected_str = OwnedValue::build_text("Alice"); assert_eq!(exec_trim(&input_str, Some(pattern_str)), expected_str); } #[test] fn test_ltrim() { let input_str = OwnedValue::build_text(" Bob and Alice "); let expected_str = OwnedValue::build_text("Bob and Alice "); assert_eq!(exec_ltrim(&input_str, None), expected_str); let input_str = OwnedValue::build_text(" Bob and Alice "); let pattern_str = OwnedValue::build_text("Bob and"); let expected_str = OwnedValue::build_text("Alice "); assert_eq!(exec_ltrim(&input_str, Some(pattern_str)), expected_str); } #[test] fn test_rtrim() { let input_str = OwnedValue::build_text(" Bob and Alice "); let expected_str = OwnedValue::build_text(" Bob and Alice"); assert_eq!(exec_rtrim(&input_str, None), expected_str); let input_str = OwnedValue::build_text(" Bob and Alice "); let pattern_str = OwnedValue::build_text("Bob and"); let expected_str = OwnedValue::build_text(" Bob and Alice"); assert_eq!(exec_rtrim(&input_str, Some(pattern_str)), expected_str); let input_str = OwnedValue::build_text(" Bob and Alice "); let pattern_str = OwnedValue::build_text("and Alice"); let expected_str = OwnedValue::build_text(" Bob"); assert_eq!(exec_rtrim(&input_str, Some(pattern_str)), expected_str); } #[test] fn test_soundex() { let input_str = OwnedValue::build_text("Pfister"); let expected_str = OwnedValue::build_text("P236"); assert_eq!(exec_soundex(&input_str), expected_str); let input_str = OwnedValue::build_text("husobee"); let expected_str = OwnedValue::build_text("H210"); assert_eq!(exec_soundex(&input_str), expected_str); let input_str = OwnedValue::build_text("Tymczak"); let expected_str = OwnedValue::build_text("T522"); assert_eq!(exec_soundex(&input_str), expected_str); let input_str = OwnedValue::build_text("Ashcraft"); let expected_str = OwnedValue::build_text("A261"); assert_eq!(exec_soundex(&input_str), expected_str); let input_str = OwnedValue::build_text("Robert"); let expected_str = OwnedValue::build_text("R163"); assert_eq!(exec_soundex(&input_str), expected_str); let input_str = OwnedValue::build_text("Rupert"); let expected_str = OwnedValue::build_text("R163"); assert_eq!(exec_soundex(&input_str), expected_str); let input_str = OwnedValue::build_text("Rubin"); let expected_str = OwnedValue::build_text("R150"); assert_eq!(exec_soundex(&input_str), expected_str); let input_str = OwnedValue::build_text("Kant"); let expected_str = OwnedValue::build_text("K530"); assert_eq!(exec_soundex(&input_str), expected_str); let input_str = OwnedValue::build_text("Knuth"); let expected_str = OwnedValue::build_text("K530"); assert_eq!(exec_soundex(&input_str), expected_str); let input_str = OwnedValue::build_text("x"); let expected_str = OwnedValue::build_text("X000"); assert_eq!(exec_soundex(&input_str), expected_str); let input_str = OwnedValue::build_text("ι—ͺη”΅δΊ”θΏžιž­"); let expected_str = OwnedValue::build_text("?000"); assert_eq!(exec_soundex(&input_str), expected_str); } #[test] fn test_upper_case() { let input_str = OwnedValue::build_text("Limbo"); let expected_str = OwnedValue::build_text("LIMBO"); assert_eq!(exec_upper(&input_str).unwrap(), expected_str); let input_int = OwnedValue::Integer(10); assert_eq!(exec_upper(&input_int).unwrap(), input_int); assert_eq!(exec_upper(&OwnedValue::Null).unwrap(), OwnedValue::Null) } #[test] fn test_lower_case() { let input_str = OwnedValue::build_text("Limbo"); let expected_str = OwnedValue::build_text("limbo"); assert_eq!(exec_lower(&input_str).unwrap(), expected_str); let input_int = OwnedValue::Integer(10); assert_eq!(exec_lower(&input_int).unwrap(), input_int); assert_eq!(exec_lower(&OwnedValue::Null).unwrap(), OwnedValue::Null) } #[test] fn test_hex() { let input_str = OwnedValue::build_text("limbo"); let expected_val = OwnedValue::build_text("6C696D626F"); assert_eq!(exec_hex(&input_str), expected_val); let input_int = OwnedValue::Integer(100); let expected_val = OwnedValue::build_text("313030"); assert_eq!(exec_hex(&input_int), expected_val); let input_float = OwnedValue::Float(12.34); let expected_val = OwnedValue::build_text("31322E3334"); assert_eq!(exec_hex(&input_float), expected_val); } #[test] fn test_unhex() { let input = OwnedValue::build_text("6f"); let expected = OwnedValue::Blob(Rc::new(vec![0x6f])); assert_eq!(exec_unhex(&input, None), expected); let input = OwnedValue::build_text("6f"); let expected = OwnedValue::Blob(Rc::new(vec![0x6f])); assert_eq!(exec_unhex(&input, None), expected); let input = OwnedValue::build_text("611"); let expected = OwnedValue::Null; assert_eq!(exec_unhex(&input, None), expected); let input = OwnedValue::build_text(""); let expected = OwnedValue::Blob(Rc::new(vec![])); assert_eq!(exec_unhex(&input, None), expected); let input = OwnedValue::build_text("61x"); let expected = OwnedValue::Null; assert_eq!(exec_unhex(&input, None), expected); let input = OwnedValue::Null; let expected = OwnedValue::Null; assert_eq!(exec_unhex(&input, None), expected); } #[test] fn test_abs() { let int_positive_reg = OwnedValue::Integer(10); let int_negative_reg = OwnedValue::Integer(-10); assert_eq!(exec_abs(&int_positive_reg).unwrap(), int_positive_reg); assert_eq!(exec_abs(&int_negative_reg).unwrap(), int_positive_reg); let float_positive_reg = OwnedValue::Integer(10); let float_negative_reg = OwnedValue::Integer(-10); assert_eq!(exec_abs(&float_positive_reg).unwrap(), float_positive_reg); assert_eq!(exec_abs(&float_negative_reg).unwrap(), float_positive_reg); assert_eq!( exec_abs(&OwnedValue::build_text("a")).unwrap(), OwnedValue::Float(0.0) ); assert_eq!(exec_abs(&OwnedValue::Null).unwrap(), OwnedValue::Null); // ABS(i64::MIN) should return RuntimeError assert!(exec_abs(&OwnedValue::Integer(i64::MIN)).is_err()); } #[test] fn test_char() { assert_eq!( exec_char(vec![OwnedValue::Integer(108), OwnedValue::Integer(105)]), OwnedValue::build_text("li") ); assert_eq!(exec_char(vec![]), OwnedValue::build_text("")); assert_eq!( exec_char(vec![OwnedValue::Null]), OwnedValue::build_text("") ); assert_eq!( exec_char(vec![OwnedValue::build_text("a")]), OwnedValue::build_text("") ); } #[test] fn test_like_with_escape_or_regexmeta_chars() { assert!(exec_like(None, r#"\%A"#, r#"\A"#)); assert!(exec_like(None, "%a%a", "aaaa")); } #[test] fn test_like_no_cache() { assert!(exec_like(None, "a%", "aaaa")); assert!(exec_like(None, "%a%a", "aaaa")); assert!(!exec_like(None, "%a.a", "aaaa")); assert!(!exec_like(None, "a.a%", "aaaa")); assert!(!exec_like(None, "%a.ab", "aaaa")); } #[test] fn test_like_with_cache() { let mut cache = HashMap::new(); assert!(exec_like(Some(&mut cache), "a%", "aaaa")); assert!(exec_like(Some(&mut cache), "%a%a", "aaaa")); assert!(!exec_like(Some(&mut cache), "%a.a", "aaaa")); assert!(!exec_like(Some(&mut cache), "a.a%", "aaaa")); assert!(!exec_like(Some(&mut cache), "%a.ab", "aaaa")); // again after values have been cached assert!(exec_like(Some(&mut cache), "a%", "aaaa")); assert!(exec_like(Some(&mut cache), "%a%a", "aaaa")); assert!(!exec_like(Some(&mut cache), "%a.a", "aaaa")); assert!(!exec_like(Some(&mut cache), "a.a%", "aaaa")); assert!(!exec_like(Some(&mut cache), "%a.ab", "aaaa")); } #[test] fn test_random() { match exec_random() { OwnedValue::Integer(value) => { // Check that the value is within the range of i64 assert!( (i64::MIN..=i64::MAX).contains(&value), "Random number out of range" ); } _ => panic!("exec_random did not return an Integer variant"), } } #[test] fn test_exec_randomblob() { struct TestCase { input: OwnedValue, expected_len: usize, } let test_cases = vec![ TestCase { input: OwnedValue::Integer(5), expected_len: 5, }, TestCase { input: OwnedValue::Integer(0), expected_len: 1, }, TestCase { input: OwnedValue::Integer(-1), expected_len: 1, }, TestCase { input: OwnedValue::build_text(""), expected_len: 1, }, TestCase { input: OwnedValue::build_text("5"), expected_len: 5, }, TestCase { input: OwnedValue::build_text("0"), expected_len: 1, }, TestCase { input: OwnedValue::build_text("-1"), expected_len: 1, }, TestCase { input: OwnedValue::Float(2.9), expected_len: 2, }, TestCase { input: OwnedValue::Float(-3.15), expected_len: 1, }, TestCase { input: OwnedValue::Null, expected_len: 1, }, ]; for test_case in &test_cases { let result = exec_randomblob(&test_case.input); match result { OwnedValue::Blob(blob) => { assert_eq!(blob.len(), test_case.expected_len); } _ => panic!("exec_randomblob did not return a Blob variant"), } } } #[test] fn test_exec_round() { let input_val = OwnedValue::Float(123.456); let expected_val = OwnedValue::Float(123.0); assert_eq!(exec_round(&input_val, None), expected_val); let input_val = OwnedValue::Float(123.456); let precision_val = OwnedValue::Integer(2); let expected_val = OwnedValue::Float(123.46); assert_eq!(exec_round(&input_val, Some(precision_val)), expected_val); let input_val = OwnedValue::Float(123.456); let precision_val = OwnedValue::build_text("1"); let expected_val = OwnedValue::Float(123.5); assert_eq!(exec_round(&input_val, Some(precision_val)), expected_val); let input_val = OwnedValue::build_text("123.456"); let precision_val = OwnedValue::Integer(2); let expected_val = OwnedValue::Float(123.46); assert_eq!(exec_round(&input_val, Some(precision_val)), expected_val); let input_val = OwnedValue::Integer(123); let precision_val = OwnedValue::Integer(1); let expected_val = OwnedValue::Float(123.0); assert_eq!(exec_round(&input_val, Some(precision_val)), expected_val); let input_val = OwnedValue::Float(100.123); let expected_val = OwnedValue::Float(100.0); assert_eq!(exec_round(&input_val, None), expected_val); let input_val = OwnedValue::Float(100.123); let expected_val = OwnedValue::Null; assert_eq!(exec_round(&input_val, Some(OwnedValue::Null)), expected_val); } #[test] fn test_exec_if() { let reg = OwnedValue::Integer(0); assert!(!exec_if(®, false, false)); assert!(exec_if(®, false, true)); let reg = OwnedValue::Integer(1); assert!(exec_if(®, false, false)); assert!(!exec_if(®, false, true)); let reg = OwnedValue::Null; assert!(!exec_if(®, false, false)); assert!(!exec_if(®, false, true)); let reg = OwnedValue::Null; assert!(exec_if(®, true, false)); assert!(exec_if(®, true, true)); let reg = OwnedValue::Null; assert!(!exec_if(®, false, false)); assert!(!exec_if(®, false, true)); } #[test] fn test_nullif() { assert_eq!( exec_nullif(&OwnedValue::Integer(1), &OwnedValue::Integer(1)), OwnedValue::Null ); assert_eq!( exec_nullif(&OwnedValue::Float(1.1), &OwnedValue::Float(1.1)), OwnedValue::Null ); assert_eq!( exec_nullif( &OwnedValue::build_text("limbo"), &OwnedValue::build_text("limbo") ), OwnedValue::Null ); assert_eq!( exec_nullif(&OwnedValue::Integer(1), &OwnedValue::Integer(2)), OwnedValue::Integer(1) ); assert_eq!( exec_nullif(&OwnedValue::Float(1.1), &OwnedValue::Float(1.2)), OwnedValue::Float(1.1) ); assert_eq!( exec_nullif( &OwnedValue::build_text("limbo"), &OwnedValue::build_text("limb") ), OwnedValue::build_text("limbo") ); } #[test] fn test_substring() { let str_value = OwnedValue::build_text("limbo"); let start_value = OwnedValue::Integer(1); let length_value = OwnedValue::Integer(3); let expected_val = OwnedValue::build_text("lim"); assert_eq!( exec_substring(&str_value, &start_value, Some(&length_value)), expected_val ); let str_value = OwnedValue::build_text("limbo"); let start_value = OwnedValue::Integer(1); let length_value = OwnedValue::Integer(10); let expected_val = OwnedValue::build_text("limbo"); assert_eq!( exec_substring(&str_value, &start_value, Some(&length_value)), expected_val ); let str_value = OwnedValue::build_text("limbo"); let start_value = OwnedValue::Integer(10); let length_value = OwnedValue::Integer(3); let expected_val = OwnedValue::build_text(""); assert_eq!( exec_substring(&str_value, &start_value, Some(&length_value)), expected_val ); let str_value = OwnedValue::build_text("limbo"); let start_value = OwnedValue::Integer(3); let length_value = OwnedValue::Null; let expected_val = OwnedValue::build_text("mbo"); assert_eq!( exec_substring(&str_value, &start_value, Some(&length_value)), expected_val ); let str_value = OwnedValue::build_text("limbo"); let start_value = OwnedValue::Integer(10); let length_value = OwnedValue::Null; let expected_val = OwnedValue::build_text(""); assert_eq!( exec_substring(&str_value, &start_value, Some(&length_value)), expected_val ); } #[test] fn test_exec_instr() { let input = OwnedValue::build_text("limbo"); let pattern = OwnedValue::build_text("im"); let expected = OwnedValue::Integer(2); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::build_text("limbo"); let pattern = OwnedValue::build_text("limbo"); let expected = OwnedValue::Integer(1); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::build_text("limbo"); let pattern = OwnedValue::build_text("o"); let expected = OwnedValue::Integer(5); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::build_text("liiiiimbo"); let pattern = OwnedValue::build_text("ii"); let expected = OwnedValue::Integer(2); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::build_text("limbo"); let pattern = OwnedValue::build_text("limboX"); let expected = OwnedValue::Integer(0); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::build_text("limbo"); let pattern = OwnedValue::build_text(""); let expected = OwnedValue::Integer(1); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::build_text(""); let pattern = OwnedValue::build_text("limbo"); let expected = OwnedValue::Integer(0); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::build_text(""); let pattern = OwnedValue::build_text(""); let expected = OwnedValue::Integer(1); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::Null; let pattern = OwnedValue::Null; let expected = OwnedValue::Null; assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::build_text("limbo"); let pattern = OwnedValue::Null; let expected = OwnedValue::Null; assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::Null; let pattern = OwnedValue::build_text("limbo"); let expected = OwnedValue::Null; assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::Integer(123); let pattern = OwnedValue::Integer(2); let expected = OwnedValue::Integer(2); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::Integer(123); let pattern = OwnedValue::Integer(5); let expected = OwnedValue::Integer(0); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::Float(12.34); let pattern = OwnedValue::Float(2.3); let expected = OwnedValue::Integer(2); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::Float(12.34); let pattern = OwnedValue::Float(5.6); let expected = OwnedValue::Integer(0); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::Float(12.34); let pattern = OwnedValue::build_text("."); let expected = OwnedValue::Integer(3); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::Blob(Rc::new(vec![1, 2, 3, 4, 5])); let pattern = OwnedValue::Blob(Rc::new(vec![3, 4])); let expected = OwnedValue::Integer(3); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::Blob(Rc::new(vec![1, 2, 3, 4, 5])); let pattern = OwnedValue::Blob(Rc::new(vec![3, 2])); let expected = OwnedValue::Integer(0); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::Blob(Rc::new(vec![0x61, 0x62, 0x63, 0x64, 0x65])); let pattern = OwnedValue::build_text("cd"); let expected = OwnedValue::Integer(3); assert_eq!(exec_instr(&input, &pattern), expected); let input = OwnedValue::build_text("abcde"); let pattern = OwnedValue::Blob(Rc::new(vec![0x63, 0x64])); let expected = OwnedValue::Integer(3); assert_eq!(exec_instr(&input, &pattern), expected); } #[test] fn test_exec_sign() { let input = OwnedValue::Integer(42); let expected = Some(OwnedValue::Integer(1)); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::Integer(-42); let expected = Some(OwnedValue::Integer(-1)); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::Integer(0); let expected = Some(OwnedValue::Integer(0)); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::Float(0.0); let expected = Some(OwnedValue::Integer(0)); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::Float(0.1); let expected = Some(OwnedValue::Integer(1)); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::Float(42.0); let expected = Some(OwnedValue::Integer(1)); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::Float(-42.0); let expected = Some(OwnedValue::Integer(-1)); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::build_text("abc"); let expected = Some(OwnedValue::Null); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::build_text("42"); let expected = Some(OwnedValue::Integer(1)); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::build_text("-42"); let expected = Some(OwnedValue::Integer(-1)); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::build_text("0"); let expected = Some(OwnedValue::Integer(0)); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::Blob(Rc::new(b"abc".to_vec())); let expected = Some(OwnedValue::Null); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::Blob(Rc::new(b"42".to_vec())); let expected = Some(OwnedValue::Integer(1)); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::Blob(Rc::new(b"-42".to_vec())); let expected = Some(OwnedValue::Integer(-1)); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::Blob(Rc::new(b"0".to_vec())); let expected = Some(OwnedValue::Integer(0)); assert_eq!(exec_sign(&input), expected); let input = OwnedValue::Null; let expected = Some(OwnedValue::Null); assert_eq!(exec_sign(&input), expected); } #[test] fn test_exec_zeroblob() { let input = OwnedValue::Integer(0); let expected = OwnedValue::Blob(Rc::new(vec![])); assert_eq!(exec_zeroblob(&input), expected); let input = OwnedValue::Null; let expected = OwnedValue::Blob(Rc::new(vec![])); assert_eq!(exec_zeroblob(&input), expected); let input = OwnedValue::Integer(4); let expected = OwnedValue::Blob(Rc::new(vec![0; 4])); assert_eq!(exec_zeroblob(&input), expected); let input = OwnedValue::Integer(-1); let expected = OwnedValue::Blob(Rc::new(vec![])); assert_eq!(exec_zeroblob(&input), expected); let input = OwnedValue::build_text("5"); let expected = OwnedValue::Blob(Rc::new(vec![0; 5])); assert_eq!(exec_zeroblob(&input), expected); let input = OwnedValue::build_text("-5"); let expected = OwnedValue::Blob(Rc::new(vec![])); assert_eq!(exec_zeroblob(&input), expected); let input = OwnedValue::build_text("text"); let expected = OwnedValue::Blob(Rc::new(vec![])); assert_eq!(exec_zeroblob(&input), expected); let input = OwnedValue::Float(2.6); let expected = OwnedValue::Blob(Rc::new(vec![0; 2])); assert_eq!(exec_zeroblob(&input), expected); let input = OwnedValue::Blob(Rc::new(vec![1])); let expected = OwnedValue::Blob(Rc::new(vec![])); assert_eq!(exec_zeroblob(&input), expected); } #[test] fn test_execute_sqlite_version() { let version_integer = 3046001; let expected = "3.46.1"; assert_eq!(execute_sqlite_version(version_integer), expected); } #[test] fn test_replace() { let input_str = OwnedValue::build_text("bob"); let pattern_str = OwnedValue::build_text("b"); let replace_str = OwnedValue::build_text("a"); let expected_str = OwnedValue::build_text("aoa"); assert_eq!( exec_replace(&input_str, &pattern_str, &replace_str), expected_str ); let input_str = OwnedValue::build_text("bob"); let pattern_str = OwnedValue::build_text("b"); let replace_str = OwnedValue::build_text(""); let expected_str = OwnedValue::build_text("o"); assert_eq!( exec_replace(&input_str, &pattern_str, &replace_str), expected_str ); let input_str = OwnedValue::build_text("bob"); let pattern_str = OwnedValue::build_text("b"); let replace_str = OwnedValue::build_text("abc"); let expected_str = OwnedValue::build_text("abcoabc"); assert_eq!( exec_replace(&input_str, &pattern_str, &replace_str), expected_str ); let input_str = OwnedValue::build_text("bob"); let pattern_str = OwnedValue::build_text("a"); let replace_str = OwnedValue::build_text("b"); let expected_str = OwnedValue::build_text("bob"); assert_eq!( exec_replace(&input_str, &pattern_str, &replace_str), expected_str ); let input_str = OwnedValue::build_text("bob"); let pattern_str = OwnedValue::build_text(""); let replace_str = OwnedValue::build_text("a"); let expected_str = OwnedValue::build_text("bob"); assert_eq!( exec_replace(&input_str, &pattern_str, &replace_str), expected_str ); let input_str = OwnedValue::build_text("bob"); let pattern_str = OwnedValue::Null; let replace_str = OwnedValue::build_text("a"); let expected_str = OwnedValue::Null; assert_eq!( exec_replace(&input_str, &pattern_str, &replace_str), expected_str ); let input_str = OwnedValue::build_text("bo5"); let pattern_str = OwnedValue::Integer(5); let replace_str = OwnedValue::build_text("a"); let expected_str = OwnedValue::build_text("boa"); assert_eq!( exec_replace(&input_str, &pattern_str, &replace_str), expected_str ); let input_str = OwnedValue::build_text("bo5.0"); let pattern_str = OwnedValue::Float(5.0); let replace_str = OwnedValue::build_text("a"); let expected_str = OwnedValue::build_text("boa"); assert_eq!( exec_replace(&input_str, &pattern_str, &replace_str), expected_str ); let input_str = OwnedValue::build_text("bo5"); let pattern_str = OwnedValue::Float(5.0); let replace_str = OwnedValue::build_text("a"); let expected_str = OwnedValue::build_text("bo5"); assert_eq!( exec_replace(&input_str, &pattern_str, &replace_str), expected_str ); let input_str = OwnedValue::build_text("bo5.0"); let pattern_str = OwnedValue::Float(5.0); let replace_str = OwnedValue::Float(6.0); let expected_str = OwnedValue::build_text("bo6.0"); assert_eq!( exec_replace(&input_str, &pattern_str, &replace_str), expected_str ); // todo: change this test to use (0.1 + 0.2) instead of 0.3 when decimals are implemented. let input_str = OwnedValue::build_text("tes3"); let pattern_str = OwnedValue::Integer(3); let replace_str = OwnedValue::Agg(Box::new(AggContext::Sum(OwnedValue::Float(0.3)))); let expected_str = OwnedValue::build_text("tes0.3"); assert_eq!( exec_replace(&input_str, &pattern_str, &replace_str), expected_str ); } #[test] fn test_bitfield() { let mut bitfield = Bitfield::<4>::new(); for i in 0..256 { bitfield.set(i); assert!(bitfield.get(i)); for j in 0..i { assert!(bitfield.get(j)); } for j in i + 1..256 { assert!(!bitfield.get(j)); } } for i in 0..256 { bitfield.unset(i); assert!(!bitfield.get(i)); } } }