diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index 5cf2e6cd2..2b71ee716 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -1,46 +1,55 @@ #![allow(unused_variables)] -use crate::error::{LimboError, SQLITE_CONSTRAINT, SQLITE_CONSTRAINT_PRIMARYKEY}; -use crate::ext::ExtValue; -use crate::function::{AggFunc, ExtFunc, MathFunc, MathFuncArity, ScalarFunc, VectorFunc}; -use crate::functions::datetime::{ - exec_date, exec_datetime_full, exec_julianday, exec_strftime, exec_time, exec_unixepoch, +use crate::{ + error::{LimboError, SQLITE_CONSTRAINT, SQLITE_CONSTRAINT_PRIMARYKEY}, + ext::ExtValue, + function::{AggFunc, ExtFunc, MathFunc, MathFuncArity, ScalarFunc, VectorFunc}, + functions::{ + datetime::{ + exec_date, exec_datetime_full, exec_julianday, exec_strftime, exec_time, exec_unixepoch, + }, + printf::exec_printf, + }, }; -use crate::functions::printf::exec_printf; use std::{borrow::BorrowMut, rc::Rc}; -use crate::pseudo::PseudoCursor; -use crate::result::LimboResult; +use crate::{pseudo::PseudoCursor, result::LimboResult}; -use crate::schema::{affinity, Affinity}; -use crate::storage::btree::{BTreeCursor, BTreeKey}; +use crate::{ + schema::{affinity, Affinity}, + storage::btree::{BTreeCursor, BTreeKey}, +}; -use crate::storage::wal::CheckpointResult; -use crate::types::{ - AggContext, Cursor, CursorResult, ExternalAggState, OwnedValue, OwnedValueType, SeekKey, SeekOp, +use crate::{ + storage::wal::CheckpointResult, + types::{ + AggContext, Cursor, CursorResult, ExternalAggState, OwnedValue, OwnedValueType, SeekKey, + SeekOp, + }, + util::{ + cast_real_to_integer, cast_text_to_integer, cast_text_to_numeric, cast_text_to_real, + checked_cast_text_to_numeric, parse_schema_rows, RoundToPrecision, + }, + vdbe::{ + builder::CursorType, + insn::{IdxInsertFlags, Insn}, + }, + vector::{vector32, vector64, vector_distance_cos, vector_extract}, }; -use crate::util::{ - cast_real_to_integer, cast_text_to_integer, cast_text_to_numeric, cast_text_to_real, - checked_cast_text_to_numeric, parse_schema_rows, RoundToPrecision, -}; -use crate::vdbe::builder::CursorType; -use crate::vdbe::insn::{IdxInsertFlags, Insn}; -use crate::vector::{vector32, vector64, vector_distance_cos, vector_extract}; use crate::{info, MvCursor, RefValue, Row, StepResult, TransactionState}; -use super::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, RegisterOrLiteral, +use super::{ + insn::{Cookie, RegisterOrLiteral}, + HaltState, }; -use super::HaltState; use rand::thread_rng; -use super::likeop::{construct_like_escape_arg, exec_glob, exec_like_with_escape}; -use super::sorter::Sorter; +use super::{ + likeop::{construct_like_escape_arg, exec_glob, exec_like_with_escape}, + sorter::Sorter, +}; use regex::{Regex, RegexBuilder}; -use std::cell::RefCell; -use std::collections::HashMap; +use std::{cell::RefCell, collections::HashMap}; #[cfg(feature = "json")] use crate::{ @@ -5355,8 +5364,888 @@ fn exec_likely(reg: &OwnedValue) -> OwnedValue { reg.clone() } +pub fn exec_add(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { + let result = match (lhs, rhs) { + (OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => { + let result = lhs.overflowing_add(*rhs); + if result.1 { + OwnedValue::Float(*lhs as f64 + *rhs as f64) + } else { + OwnedValue::Integer(result.0) + } + } + (OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(lhs + rhs), + (OwnedValue::Float(f), OwnedValue::Integer(i)) + | (OwnedValue::Integer(i), OwnedValue::Float(f)) => OwnedValue::Float(*f + *i as f64), + (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, + (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_add( + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), + ), + (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { + exec_add(&cast_text_to_numeric(text.as_str()), other) + } + _ => todo!(), + }; + match result { + OwnedValue::Float(f) if f.is_nan() => OwnedValue::Null, + _ => result, + } +} + +pub fn exec_subtract(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { + let result = match (lhs, rhs) { + (OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => { + let result = lhs.overflowing_sub(*rhs); + if result.1 { + OwnedValue::Float(*lhs as f64 - *rhs as f64) + } else { + OwnedValue::Integer(result.0) + } + } + (OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(lhs - rhs), + (OwnedValue::Float(lhs), OwnedValue::Integer(rhs)) => OwnedValue::Float(lhs - *rhs as f64), + (OwnedValue::Integer(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(*lhs as f64 - rhs), + (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, + (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_subtract( + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), + ), + (OwnedValue::Text(text), other) => { + exec_subtract(&cast_text_to_numeric(text.as_str()), other) + } + (other, OwnedValue::Text(text)) => { + exec_subtract(other, &cast_text_to_numeric(text.as_str())) + } + _ => todo!(), + }; + match result { + OwnedValue::Float(f) if f.is_nan() => OwnedValue::Null, + _ => result, + } +} + +pub fn exec_multiply(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { + let result = match (lhs, rhs) { + (OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => { + let result = lhs.overflowing_mul(*rhs); + if result.1 { + OwnedValue::Float(*lhs as f64 * *rhs as f64) + } else { + OwnedValue::Integer(result.0) + } + } + (OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(lhs * rhs), + (OwnedValue::Integer(i), OwnedValue::Float(f)) + | (OwnedValue::Float(f), OwnedValue::Integer(i)) => OwnedValue::Float(*i as f64 * { *f }), + (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, + (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_multiply( + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), + ), + (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { + exec_multiply(&cast_text_to_numeric(text.as_str()), other) + } + + _ => todo!(), + }; + match result { + OwnedValue::Float(f) if f.is_nan() => OwnedValue::Null, + _ => result, + } +} + +pub fn exec_divide(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { + let result = match (lhs, rhs) { + (_, OwnedValue::Integer(0)) | (_, OwnedValue::Float(0.0)) => OwnedValue::Null, + (OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => { + let result = lhs.overflowing_div(*rhs); + if result.1 { + OwnedValue::Float(*lhs as f64 / *rhs as f64) + } else { + OwnedValue::Integer(result.0) + } + } + (OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(lhs / rhs), + (OwnedValue::Float(lhs), OwnedValue::Integer(rhs)) => OwnedValue::Float(lhs / *rhs as f64), + (OwnedValue::Integer(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(*lhs as f64 / rhs), + (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, + (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_divide( + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), + ), + (OwnedValue::Text(text), other) => exec_divide(&cast_text_to_numeric(text.as_str()), other), + (other, OwnedValue::Text(text)) => exec_divide(other, &cast_text_to_numeric(text.as_str())), + _ => todo!(), + }; + match result { + OwnedValue::Float(f) if f.is_nan() => OwnedValue::Null, + _ => result, + } +} + +pub fn exec_bit_and(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { + match (lhs, rhs) { + (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, + (_, OwnedValue::Integer(0)) + | (OwnedValue::Integer(0), _) + | (_, OwnedValue::Float(0.0)) + | (OwnedValue::Float(0.0), _) => OwnedValue::Integer(0), + (OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => OwnedValue::Integer(lh & rh), + (OwnedValue::Float(lh), OwnedValue::Float(rh)) => { + OwnedValue::Integer(*lh as i64 & *rh as i64) + } + (OwnedValue::Float(lh), OwnedValue::Integer(rh)) => OwnedValue::Integer(*lh as i64 & rh), + (OwnedValue::Integer(lh), OwnedValue::Float(rh)) => OwnedValue::Integer(lh & *rh as i64), + (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_bit_and( + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), + ), + (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { + exec_bit_and(&cast_text_to_numeric(text.as_str()), other) + } + _ => todo!(), + } +} + +pub fn exec_bit_or(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { + match (lhs, rhs) { + (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, + (OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => OwnedValue::Integer(lh | rh), + (OwnedValue::Float(lh), OwnedValue::Integer(rh)) => OwnedValue::Integer(*lh as i64 | rh), + (OwnedValue::Integer(lh), OwnedValue::Float(rh)) => OwnedValue::Integer(lh | *rh as i64), + (OwnedValue::Float(lh), OwnedValue::Float(rh)) => { + OwnedValue::Integer(*lh as i64 | *rh as i64) + } + (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_bit_or( + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), + ), + (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { + exec_bit_or(&cast_text_to_numeric(text.as_str()), other) + } + _ => todo!(), + } +} + +pub fn exec_remainder(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { + match (lhs, rhs) { + (OwnedValue::Null, _) + | (_, OwnedValue::Null) + | (_, OwnedValue::Integer(0)) + | (_, OwnedValue::Float(0.0)) => OwnedValue::Null, + (OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => { + if rhs == &0 { + OwnedValue::Null + } else { + OwnedValue::Integer(lhs % rhs.abs()) + } + } + (OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => { + let rhs_int = *rhs as i64; + if rhs_int == 0 { + OwnedValue::Null + } else { + OwnedValue::Float(((*lhs as i64) % rhs_int.abs()) as f64) + } + } + (OwnedValue::Float(lhs), OwnedValue::Integer(rhs)) => { + if rhs == &0 { + OwnedValue::Null + } else { + OwnedValue::Float(((*lhs as i64) % rhs.abs()) as f64) + } + } + (OwnedValue::Integer(lhs), OwnedValue::Float(rhs)) => { + let rhs_int = *rhs as i64; + if rhs_int == 0 { + OwnedValue::Null + } else { + OwnedValue::Float((lhs % rhs_int.abs()) as f64) + } + } + (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_remainder( + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), + ), + (OwnedValue::Text(text), other) => { + exec_remainder(&cast_text_to_numeric(text.as_str()), other) + } + (other, OwnedValue::Text(text)) => { + exec_remainder(other, &cast_text_to_numeric(text.as_str())) + } + other => todo!("remainder not implemented for: {:?} {:?}", lhs, other), + } +} + +pub fn exec_bit_not(reg: &OwnedValue) -> OwnedValue { + match reg { + OwnedValue::Null => OwnedValue::Null, + OwnedValue::Integer(i) => OwnedValue::Integer(!i), + OwnedValue::Float(f) => OwnedValue::Integer(!(*f as i64)), + OwnedValue::Text(text) => exec_bit_not(&cast_text_to_numeric(text.as_str())), + _ => todo!(), + } +} + +pub fn exec_shift_left(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { + match (lhs, rhs) { + (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, + (OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => { + OwnedValue::Integer(compute_shl(*lh, *rh)) + } + (OwnedValue::Float(lh), OwnedValue::Integer(rh)) => { + OwnedValue::Integer(compute_shl(*lh as i64, *rh)) + } + (OwnedValue::Integer(lh), OwnedValue::Float(rh)) => { + OwnedValue::Integer(compute_shl(*lh, *rh as i64)) + } + (OwnedValue::Float(lh), OwnedValue::Float(rh)) => { + OwnedValue::Integer(compute_shl(*lh as i64, *rh as i64)) + } + (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_shift_left( + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), + ), + (OwnedValue::Text(text), other) => { + exec_shift_left(&cast_text_to_numeric(text.as_str()), other) + } + (other, OwnedValue::Text(text)) => { + exec_shift_left(other, &cast_text_to_numeric(text.as_str())) + } + _ => todo!(), + } +} + +fn compute_shl(lhs: i64, rhs: i64) -> i64 { + if rhs == 0 { + lhs + } else if rhs > 0 { + // for positive shifts, if it's too large return 0 + if rhs >= 64 { + 0 + } else { + lhs << rhs + } + } else { + // for negative shifts, check if it's i64::MIN to avoid overflow on negation + if rhs == i64::MIN || rhs <= -64 { + if lhs < 0 { + -1 + } else { + 0 + } + } else { + lhs >> (-rhs) + } + } +} + +pub fn exec_shift_right(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { + match (lhs, rhs) { + (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, + (OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => { + OwnedValue::Integer(compute_shr(*lh, *rh)) + } + (OwnedValue::Float(lh), OwnedValue::Integer(rh)) => { + OwnedValue::Integer(compute_shr(*lh as i64, *rh)) + } + (OwnedValue::Integer(lh), OwnedValue::Float(rh)) => { + OwnedValue::Integer(compute_shr(*lh, *rh as i64)) + } + (OwnedValue::Float(lh), OwnedValue::Float(rh)) => { + OwnedValue::Integer(compute_shr(*lh as i64, *rh as i64)) + } + (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_shift_right( + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), + ), + (OwnedValue::Text(text), other) => { + exec_shift_right(&cast_text_to_numeric(text.as_str()), other) + } + (other, OwnedValue::Text(text)) => { + exec_shift_right(other, &cast_text_to_numeric(text.as_str())) + } + _ => todo!(), + } +} + +// compute binary shift to the right if rhs >= 0 and binary shift to the left - if rhs < 0 +// note, that binary shift to the right is sign-extended +fn compute_shr(lhs: i64, rhs: i64) -> i64 { + if rhs == 0 { + lhs + } else if rhs > 0 { + // for positive right shifts + if rhs >= 64 { + if lhs < 0 { + -1 + } else { + 0 + } + } else { + lhs >> rhs + } + } else { + // for negative right shifts, check if it's i64::MIN to avoid overflow + if rhs == i64::MIN || -rhs >= 64 { + 0 + } else { + lhs << (-rhs) + } + } +} + +pub fn exec_boolean_not(reg: &OwnedValue) -> OwnedValue { + match reg { + OwnedValue::Null => OwnedValue::Null, + OwnedValue::Integer(i) => OwnedValue::Integer((*i == 0) as i64), + OwnedValue::Float(f) => OwnedValue::Integer((*f == 0.0) as i64), + OwnedValue::Text(text) => exec_boolean_not(&cast_text_to_numeric(text.as_str())), + _ => todo!(), + } +} +pub fn exec_concat(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { + match (lhs, rhs) { + (OwnedValue::Text(lhs_text), OwnedValue::Text(rhs_text)) => { + OwnedValue::build_text(&(lhs_text.as_str().to_string() + rhs_text.as_str())) + } + (OwnedValue::Text(lhs_text), OwnedValue::Integer(rhs_int)) => { + OwnedValue::build_text(&(lhs_text.as_str().to_string() + &rhs_int.to_string())) + } + (OwnedValue::Text(lhs_text), OwnedValue::Float(rhs_float)) => { + OwnedValue::build_text(&(lhs_text.as_str().to_string() + &rhs_float.to_string())) + } + (OwnedValue::Integer(lhs_int), OwnedValue::Text(rhs_text)) => { + OwnedValue::build_text(&(lhs_int.to_string() + rhs_text.as_str())) + } + (OwnedValue::Integer(lhs_int), OwnedValue::Integer(rhs_int)) => { + OwnedValue::build_text(&(lhs_int.to_string() + &rhs_int.to_string())) + } + (OwnedValue::Integer(lhs_int), OwnedValue::Float(rhs_float)) => { + OwnedValue::build_text(&(lhs_int.to_string() + &rhs_float.to_string())) + } + (OwnedValue::Float(lhs_float), OwnedValue::Text(rhs_text)) => { + OwnedValue::build_text(&(lhs_float.to_string() + rhs_text.as_str())) + } + (OwnedValue::Float(lhs_float), OwnedValue::Integer(rhs_int)) => { + OwnedValue::build_text(&(lhs_float.to_string() + &rhs_int.to_string())) + } + (OwnedValue::Float(lhs_float), OwnedValue::Float(rhs_float)) => { + OwnedValue::build_text(&(lhs_float.to_string() + &rhs_float.to_string())) + } + (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, + (OwnedValue::Blob(_), _) | (_, OwnedValue::Blob(_)) => { + todo!("TODO: Handle Blob conversion to String") + } + } +} + +pub fn exec_and(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { + match (lhs, rhs) { + (_, OwnedValue::Integer(0)) + | (OwnedValue::Integer(0), _) + | (_, OwnedValue::Float(0.0)) + | (OwnedValue::Float(0.0), _) => OwnedValue::Integer(0), + (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, + (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_and( + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), + ), + (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { + exec_and(&cast_text_to_numeric(text.as_str()), other) + } + _ => OwnedValue::Integer(1), + } +} + +pub fn exec_or(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { + match (lhs, rhs) { + (OwnedValue::Null, OwnedValue::Null) + | (OwnedValue::Null, OwnedValue::Float(0.0)) + | (OwnedValue::Float(0.0), OwnedValue::Null) + | (OwnedValue::Null, OwnedValue::Integer(0)) + | (OwnedValue::Integer(0), OwnedValue::Null) => OwnedValue::Null, + (OwnedValue::Float(0.0), OwnedValue::Integer(0)) + | (OwnedValue::Integer(0), OwnedValue::Float(0.0)) + | (OwnedValue::Float(0.0), OwnedValue::Float(0.0)) + | (OwnedValue::Integer(0), OwnedValue::Integer(0)) => OwnedValue::Integer(0), + (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_or( + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), + ), + (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { + exec_or(&cast_text_to_numeric(text.as_str()), other) + } + _ => OwnedValue::Integer(1), + } +} + #[cfg(test)] mod tests { + use crate::types::{OwnedValue, Text}; + + use super::{exec_add, exec_or}; + + #[test] + fn test_exec_add() { + let inputs = vec![ + (OwnedValue::Integer(3), OwnedValue::Integer(1)), + (OwnedValue::Float(3.0), OwnedValue::Float(1.0)), + (OwnedValue::Float(3.0), OwnedValue::Integer(1)), + (OwnedValue::Integer(3), OwnedValue::Float(1.0)), + (OwnedValue::Null, OwnedValue::Null), + (OwnedValue::Null, OwnedValue::Integer(1)), + (OwnedValue::Null, OwnedValue::Float(1.0)), + (OwnedValue::Null, OwnedValue::Text(Text::from_str("2"))), + (OwnedValue::Integer(1), OwnedValue::Null), + (OwnedValue::Float(1.0), OwnedValue::Null), + (OwnedValue::Text(Text::from_str("1")), OwnedValue::Null), + ( + OwnedValue::Text(Text::from_str("1")), + OwnedValue::Text(Text::from_str("3")), + ), + ( + OwnedValue::Text(Text::from_str("1.0")), + OwnedValue::Text(Text::from_str("3.0")), + ), + ( + OwnedValue::Text(Text::from_str("1.0")), + OwnedValue::Float(3.0), + ), + ( + OwnedValue::Text(Text::from_str("1.0")), + OwnedValue::Integer(3), + ), + ( + OwnedValue::Float(1.0), + OwnedValue::Text(Text::from_str("3.0")), + ), + ( + OwnedValue::Integer(1), + OwnedValue::Text(Text::from_str("3")), + ), + ]; + + let outputs = [ + OwnedValue::Integer(4), + OwnedValue::Float(4.0), + OwnedValue::Float(4.0), + OwnedValue::Float(4.0), + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Integer(4), + OwnedValue::Float(4.0), + OwnedValue::Float(4.0), + OwnedValue::Float(4.0), + OwnedValue::Float(4.0), + OwnedValue::Float(4.0), + ]; + + assert_eq!( + inputs.len(), + outputs.len(), + "Inputs and Outputs should have same size" + ); + for (i, (lhs, rhs)) in inputs.iter().enumerate() { + assert_eq!( + exec_add(lhs, rhs), + outputs[i], + "Wrong ADD for lhs: {}, rhs: {}", + lhs, + rhs + ); + } + } + + use super::exec_subtract; + + #[test] + fn test_exec_subtract() { + let inputs = vec![ + (OwnedValue::Integer(3), OwnedValue::Integer(1)), + (OwnedValue::Float(3.0), OwnedValue::Float(1.0)), + (OwnedValue::Float(3.0), OwnedValue::Integer(1)), + (OwnedValue::Integer(3), OwnedValue::Float(1.0)), + (OwnedValue::Null, OwnedValue::Null), + (OwnedValue::Null, OwnedValue::Integer(1)), + (OwnedValue::Null, OwnedValue::Float(1.0)), + (OwnedValue::Null, OwnedValue::Text(Text::from_str("1"))), + (OwnedValue::Integer(1), OwnedValue::Null), + (OwnedValue::Float(1.0), OwnedValue::Null), + (OwnedValue::Text(Text::from_str("4")), OwnedValue::Null), + ( + OwnedValue::Text(Text::from_str("1")), + OwnedValue::Text(Text::from_str("3")), + ), + ( + OwnedValue::Text(Text::from_str("1.0")), + OwnedValue::Text(Text::from_str("3.0")), + ), + ( + OwnedValue::Text(Text::from_str("1.0")), + OwnedValue::Float(3.0), + ), + ( + OwnedValue::Text(Text::from_str("1.0")), + OwnedValue::Integer(3), + ), + ( + OwnedValue::Float(1.0), + OwnedValue::Text(Text::from_str("3.0")), + ), + ( + OwnedValue::Integer(1), + OwnedValue::Text(Text::from_str("3")), + ), + ]; + + let outputs = [ + OwnedValue::Integer(2), + OwnedValue::Float(2.0), + OwnedValue::Float(2.0), + OwnedValue::Float(2.0), + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Integer(-2), + OwnedValue::Float(-2.0), + OwnedValue::Float(-2.0), + OwnedValue::Float(-2.0), + OwnedValue::Float(-2.0), + OwnedValue::Float(-2.0), + ]; + + assert_eq!( + inputs.len(), + outputs.len(), + "Inputs and Outputs should have same size" + ); + for (i, (lhs, rhs)) in inputs.iter().enumerate() { + assert_eq!( + exec_subtract(lhs, rhs), + outputs[i], + "Wrong subtract for lhs: {}, rhs: {}", + lhs, + rhs + ); + } + } + use super::exec_multiply; + + #[test] + fn test_exec_multiply() { + let inputs = vec![ + (OwnedValue::Integer(3), OwnedValue::Integer(2)), + (OwnedValue::Float(3.0), OwnedValue::Float(2.0)), + (OwnedValue::Float(3.0), OwnedValue::Integer(2)), + (OwnedValue::Integer(3), OwnedValue::Float(2.0)), + (OwnedValue::Null, OwnedValue::Null), + (OwnedValue::Null, OwnedValue::Integer(1)), + (OwnedValue::Null, OwnedValue::Float(1.0)), + (OwnedValue::Null, OwnedValue::Text(Text::from_str("1"))), + (OwnedValue::Integer(1), OwnedValue::Null), + (OwnedValue::Float(1.0), OwnedValue::Null), + (OwnedValue::Text(Text::from_str("4")), OwnedValue::Null), + ( + OwnedValue::Text(Text::from_str("2")), + OwnedValue::Text(Text::from_str("3")), + ), + ( + OwnedValue::Text(Text::from_str("2.0")), + OwnedValue::Text(Text::from_str("3.0")), + ), + ( + OwnedValue::Text(Text::from_str("2.0")), + OwnedValue::Float(3.0), + ), + ( + OwnedValue::Text(Text::from_str("2.0")), + OwnedValue::Integer(3), + ), + ( + OwnedValue::Float(2.0), + OwnedValue::Text(Text::from_str("3.0")), + ), + ( + OwnedValue::Integer(2), + OwnedValue::Text(Text::from_str("3.0")), + ), + ]; + + let outputs = [ + OwnedValue::Integer(6), + OwnedValue::Float(6.0), + OwnedValue::Float(6.0), + OwnedValue::Float(6.0), + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Integer(6), + OwnedValue::Float(6.0), + OwnedValue::Float(6.0), + OwnedValue::Float(6.0), + OwnedValue::Float(6.0), + OwnedValue::Float(6.0), + ]; + + assert_eq!( + inputs.len(), + outputs.len(), + "Inputs and Outputs should have same size" + ); + for (i, (lhs, rhs)) in inputs.iter().enumerate() { + assert_eq!( + exec_multiply(lhs, rhs), + outputs[i], + "Wrong multiply for lhs: {}, rhs: {}", + lhs, + rhs + ); + } + } + use super::exec_divide; + + #[test] + fn test_exec_divide() { + let inputs = vec![ + (OwnedValue::Integer(1), OwnedValue::Integer(0)), + (OwnedValue::Float(1.0), OwnedValue::Float(0.0)), + (OwnedValue::Integer(i64::MIN), OwnedValue::Integer(-1)), + (OwnedValue::Float(6.0), OwnedValue::Float(2.0)), + (OwnedValue::Float(6.0), OwnedValue::Integer(2)), + (OwnedValue::Integer(6), OwnedValue::Integer(2)), + (OwnedValue::Null, OwnedValue::Integer(2)), + (OwnedValue::Integer(2), OwnedValue::Null), + (OwnedValue::Null, OwnedValue::Null), + ( + OwnedValue::Text(Text::from_str("6")), + OwnedValue::Text(Text::from_str("2")), + ), + ( + OwnedValue::Text(Text::from_str("6")), + OwnedValue::Integer(2), + ), + ]; + + let outputs = [ + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Float(9.223372036854776e18), + OwnedValue::Float(3.0), + OwnedValue::Float(3.0), + OwnedValue::Float(3.0), + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Float(3.0), + OwnedValue::Float(3.0), + ]; + + assert_eq!( + inputs.len(), + outputs.len(), + "Inputs and Outputs should have same size" + ); + for (i, (lhs, rhs)) in inputs.iter().enumerate() { + assert_eq!( + exec_divide(lhs, rhs), + outputs[i], + "Wrong divide for lhs: {}, rhs: {}", + lhs, + rhs + ); + } + } + + use super::exec_remainder; + #[test] + fn test_exec_remainder() { + let inputs = vec![ + (OwnedValue::Null, OwnedValue::Null), + (OwnedValue::Null, OwnedValue::Float(1.0)), + (OwnedValue::Null, OwnedValue::Integer(1)), + (OwnedValue::Null, OwnedValue::Text(Text::from_str("1"))), + (OwnedValue::Float(1.0), OwnedValue::Null), + (OwnedValue::Integer(1), OwnedValue::Null), + (OwnedValue::Integer(12), OwnedValue::Integer(0)), + (OwnedValue::Float(12.0), OwnedValue::Float(0.0)), + (OwnedValue::Float(12.0), OwnedValue::Integer(0)), + (OwnedValue::Integer(12), OwnedValue::Float(0.0)), + (OwnedValue::Integer(i64::MIN), OwnedValue::Integer(-1)), + (OwnedValue::Integer(12), OwnedValue::Integer(3)), + (OwnedValue::Float(12.0), OwnedValue::Float(3.0)), + (OwnedValue::Float(12.0), OwnedValue::Integer(3)), + (OwnedValue::Integer(12), OwnedValue::Float(3.0)), + (OwnedValue::Integer(12), OwnedValue::Integer(-3)), + (OwnedValue::Float(12.0), OwnedValue::Float(-3.0)), + (OwnedValue::Float(12.0), OwnedValue::Integer(-3)), + (OwnedValue::Integer(12), OwnedValue::Float(-3.0)), + ( + OwnedValue::Text(Text::from_str("12.0")), + OwnedValue::Text(Text::from_str("3.0")), + ), + ( + OwnedValue::Text(Text::from_str("12.0")), + OwnedValue::Float(3.0), + ), + ( + OwnedValue::Float(12.0), + OwnedValue::Text(Text::from_str("3.0")), + ), + ]; + let outputs = vec![ + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Float(0.0), + OwnedValue::Integer(0), + OwnedValue::Float(0.0), + OwnedValue::Float(0.0), + OwnedValue::Float(0.0), + OwnedValue::Integer(0), + OwnedValue::Float(0.0), + OwnedValue::Float(0.0), + OwnedValue::Float(0.0), + OwnedValue::Float(0.0), + OwnedValue::Float(0.0), + OwnedValue::Float(0.0), + ]; + + assert_eq!( + inputs.len(), + outputs.len(), + "Inputs and Outputs should have same size" + ); + + for (i, (lhs, rhs)) in inputs.iter().enumerate() { + assert_eq!( + exec_remainder(lhs, rhs), + outputs[i], + "Wrong remainder for lhs: {}, rhs: {}", + lhs, + rhs + ); + } + } + + use super::exec_and; + + #[test] + fn test_exec_and() { + let inputs = vec![ + (OwnedValue::Integer(0), OwnedValue::Null), + (OwnedValue::Null, OwnedValue::Integer(1)), + (OwnedValue::Null, OwnedValue::Null), + (OwnedValue::Float(0.0), OwnedValue::Null), + (OwnedValue::Integer(1), OwnedValue::Float(2.2)), + ( + OwnedValue::Integer(0), + OwnedValue::Text(Text::from_str("string")), + ), + ( + OwnedValue::Integer(0), + OwnedValue::Text(Text::from_str("1")), + ), + ( + OwnedValue::Integer(1), + OwnedValue::Text(Text::from_str("1")), + ), + ]; + let outputs = [ + OwnedValue::Integer(0), + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Integer(0), + OwnedValue::Integer(1), + OwnedValue::Integer(0), + OwnedValue::Integer(0), + OwnedValue::Integer(1), + ]; + + assert_eq!( + inputs.len(), + outputs.len(), + "Inputs and Outputs should have same size" + ); + for (i, (lhs, rhs)) in inputs.iter().enumerate() { + assert_eq!( + exec_and(lhs, rhs), + outputs[i], + "Wrong AND for lhs: {}, rhs: {}", + lhs, + rhs + ); + } + } + + #[test] + fn test_exec_or() { + let inputs = vec![ + (OwnedValue::Integer(0), OwnedValue::Null), + (OwnedValue::Null, OwnedValue::Integer(1)), + (OwnedValue::Null, OwnedValue::Null), + (OwnedValue::Float(0.0), OwnedValue::Null), + (OwnedValue::Integer(1), OwnedValue::Float(2.2)), + (OwnedValue::Float(0.0), OwnedValue::Integer(0)), + ( + OwnedValue::Integer(0), + OwnedValue::Text(Text::from_str("string")), + ), + ( + OwnedValue::Integer(0), + OwnedValue::Text(Text::from_str("1")), + ), + (OwnedValue::Integer(0), OwnedValue::Text(Text::from_str(""))), + ]; + let outputs = [ + OwnedValue::Null, + OwnedValue::Integer(1), + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Integer(1), + OwnedValue::Integer(0), + OwnedValue::Integer(0), + OwnedValue::Integer(1), + OwnedValue::Integer(0), + ]; + + assert_eq!( + inputs.len(), + outputs.len(), + "Inputs and Outputs should have same size" + ); + for (i, (lhs, rhs)) in inputs.iter().enumerate() { + assert_eq!( + exec_or(lhs, rhs), + outputs[i], + "Wrong OR for lhs: {}, rhs: {}", + lhs, + rhs + ); + } + } + use crate::vdbe::{ execute::{exec_likely, exec_replace}, Bitfield, Register, @@ -5366,7 +6255,7 @@ mod tests { 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, OwnedValue, + exec_unhex, exec_unicode, exec_upper, exec_zeroblob, execute_sqlite_version, }; use std::collections::HashMap; diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs index 607949efb..0047f9d11 100644 --- a/core/vdbe/insn.rs +++ b/core/vdbe/insn.rs @@ -1,12 +1,7 @@ -use std::num::NonZero; -use std::rc::Rc; +use std::{num::NonZero, rc::Rc}; -use super::{ - cast_text_to_numeric, execute, AggFunc, BranchOffset, CursorID, FuncCtx, InsnFunction, PageIdx, -}; -use crate::schema::BTreeTable; -use crate::storage::wal::CheckpointMode; -use crate::types::{OwnedValue, Record}; +use super::{execute, AggFunc, BranchOffset, CursorID, FuncCtx, InsnFunction, PageIdx}; +use crate::{schema::BTreeTable, storage::wal::CheckpointMode, types::Record}; use limbo_macros::Description; /// Flags provided to comparison instructions (e.g. Eq, Ne) which determine behavior related to NULL values. @@ -815,440 +810,6 @@ pub enum Insn { }, } -// TODO: Add remaining cookies. -#[derive(Description, Debug, Clone, Copy)] -pub enum Cookie { - /// The schema cookie. - SchemaVersion = 1, - /// The schema format number. Supported schema formats are 1, 2, 3, and 4. - DatabaseFormat = 2, - /// Default page cache size. - DefaultPageCacheSize = 3, - /// The page number of the largest root b-tree page when in auto-vacuum or incremental-vacuum modes, or zero otherwise. - LargestRootPageNumber = 4, - /// The database text encoding. A value of 1 means UTF-8. A value of 2 means UTF-16le. A value of 3 means UTF-16be. - DatabaseTextEncoding = 5, - /// The "user version" as read and set by the user_version pragma. - UserVersion = 6, -} - -pub fn exec_add(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { - let result = match (lhs, rhs) { - (OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => { - let result = lhs.overflowing_add(*rhs); - if result.1 { - OwnedValue::Float(*lhs as f64 + *rhs as f64) - } else { - OwnedValue::Integer(result.0) - } - } - (OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(lhs + rhs), - (OwnedValue::Float(f), OwnedValue::Integer(i)) - | (OwnedValue::Integer(i), OwnedValue::Float(f)) => OwnedValue::Float(*f + *i as f64), - (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, - (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_add( - &cast_text_to_numeric(lhs.as_str()), - &cast_text_to_numeric(rhs.as_str()), - ), - (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { - exec_add(&cast_text_to_numeric(text.as_str()), other) - } - _ => todo!(), - }; - match result { - OwnedValue::Float(f) if f.is_nan() => OwnedValue::Null, - _ => result, - } -} - -pub fn exec_subtract(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { - let result = match (lhs, rhs) { - (OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => { - let result = lhs.overflowing_sub(*rhs); - if result.1 { - OwnedValue::Float(*lhs as f64 - *rhs as f64) - } else { - OwnedValue::Integer(result.0) - } - } - (OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(lhs - rhs), - (OwnedValue::Float(lhs), OwnedValue::Integer(rhs)) => OwnedValue::Float(lhs - *rhs as f64), - (OwnedValue::Integer(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(*lhs as f64 - rhs), - (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, - (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_subtract( - &cast_text_to_numeric(lhs.as_str()), - &cast_text_to_numeric(rhs.as_str()), - ), - (OwnedValue::Text(text), other) => { - exec_subtract(&cast_text_to_numeric(text.as_str()), other) - } - (other, OwnedValue::Text(text)) => { - exec_subtract(other, &cast_text_to_numeric(text.as_str())) - } - _ => todo!(), - }; - match result { - OwnedValue::Float(f) if f.is_nan() => OwnedValue::Null, - _ => result, - } -} - -pub fn exec_multiply(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { - let result = match (lhs, rhs) { - (OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => { - let result = lhs.overflowing_mul(*rhs); - if result.1 { - OwnedValue::Float(*lhs as f64 * *rhs as f64) - } else { - OwnedValue::Integer(result.0) - } - } - (OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(lhs * rhs), - (OwnedValue::Integer(i), OwnedValue::Float(f)) - | (OwnedValue::Float(f), OwnedValue::Integer(i)) => OwnedValue::Float(*i as f64 * { *f }), - (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, - (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_multiply( - &cast_text_to_numeric(lhs.as_str()), - &cast_text_to_numeric(rhs.as_str()), - ), - (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { - exec_multiply(&cast_text_to_numeric(text.as_str()), other) - } - - _ => todo!(), - }; - match result { - OwnedValue::Float(f) if f.is_nan() => OwnedValue::Null, - _ => result, - } -} - -pub fn exec_divide(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { - let result = match (lhs, rhs) { - (_, OwnedValue::Integer(0)) | (_, OwnedValue::Float(0.0)) => OwnedValue::Null, - (OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => { - let result = lhs.overflowing_div(*rhs); - if result.1 { - OwnedValue::Float(*lhs as f64 / *rhs as f64) - } else { - OwnedValue::Integer(result.0) - } - } - (OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(lhs / rhs), - (OwnedValue::Float(lhs), OwnedValue::Integer(rhs)) => OwnedValue::Float(lhs / *rhs as f64), - (OwnedValue::Integer(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(*lhs as f64 / rhs), - (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, - (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_divide( - &cast_text_to_numeric(lhs.as_str()), - &cast_text_to_numeric(rhs.as_str()), - ), - (OwnedValue::Text(text), other) => exec_divide(&cast_text_to_numeric(text.as_str()), other), - (other, OwnedValue::Text(text)) => exec_divide(other, &cast_text_to_numeric(text.as_str())), - _ => todo!(), - }; - match result { - OwnedValue::Float(f) if f.is_nan() => OwnedValue::Null, - _ => result, - } -} - -pub fn exec_bit_and(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { - match (lhs, rhs) { - (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, - (_, OwnedValue::Integer(0)) - | (OwnedValue::Integer(0), _) - | (_, OwnedValue::Float(0.0)) - | (OwnedValue::Float(0.0), _) => OwnedValue::Integer(0), - (OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => OwnedValue::Integer(lh & rh), - (OwnedValue::Float(lh), OwnedValue::Float(rh)) => { - OwnedValue::Integer(*lh as i64 & *rh as i64) - } - (OwnedValue::Float(lh), OwnedValue::Integer(rh)) => OwnedValue::Integer(*lh as i64 & rh), - (OwnedValue::Integer(lh), OwnedValue::Float(rh)) => OwnedValue::Integer(lh & *rh as i64), - (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_bit_and( - &cast_text_to_numeric(lhs.as_str()), - &cast_text_to_numeric(rhs.as_str()), - ), - (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { - exec_bit_and(&cast_text_to_numeric(text.as_str()), other) - } - _ => todo!(), - } -} - -pub fn exec_bit_or(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { - match (lhs, rhs) { - (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, - (OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => OwnedValue::Integer(lh | rh), - (OwnedValue::Float(lh), OwnedValue::Integer(rh)) => OwnedValue::Integer(*lh as i64 | rh), - (OwnedValue::Integer(lh), OwnedValue::Float(rh)) => OwnedValue::Integer(lh | *rh as i64), - (OwnedValue::Float(lh), OwnedValue::Float(rh)) => { - OwnedValue::Integer(*lh as i64 | *rh as i64) - } - (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_bit_or( - &cast_text_to_numeric(lhs.as_str()), - &cast_text_to_numeric(rhs.as_str()), - ), - (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { - exec_bit_or(&cast_text_to_numeric(text.as_str()), other) - } - _ => todo!(), - } -} - -pub fn exec_remainder(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { - match (lhs, rhs) { - (OwnedValue::Null, _) - | (_, OwnedValue::Null) - | (_, OwnedValue::Integer(0)) - | (_, OwnedValue::Float(0.0)) => OwnedValue::Null, - (OwnedValue::Integer(lhs), OwnedValue::Integer(rhs)) => { - if rhs == &0 { - OwnedValue::Null - } else { - OwnedValue::Integer(lhs % rhs.abs()) - } - } - (OwnedValue::Float(lhs), OwnedValue::Float(rhs)) => { - let rhs_int = *rhs as i64; - if rhs_int == 0 { - OwnedValue::Null - } else { - OwnedValue::Float(((*lhs as i64) % rhs_int.abs()) as f64) - } - } - (OwnedValue::Float(lhs), OwnedValue::Integer(rhs)) => { - if rhs == &0 { - OwnedValue::Null - } else { - OwnedValue::Float(((*lhs as i64) % rhs.abs()) as f64) - } - } - (OwnedValue::Integer(lhs), OwnedValue::Float(rhs)) => { - let rhs_int = *rhs as i64; - if rhs_int == 0 { - OwnedValue::Null - } else { - OwnedValue::Float((lhs % rhs_int.abs()) as f64) - } - } - (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_remainder( - &cast_text_to_numeric(lhs.as_str()), - &cast_text_to_numeric(rhs.as_str()), - ), - (OwnedValue::Text(text), other) => { - exec_remainder(&cast_text_to_numeric(text.as_str()), other) - } - (other, OwnedValue::Text(text)) => { - exec_remainder(other, &cast_text_to_numeric(text.as_str())) - } - other => todo!("remainder not implemented for: {:?} {:?}", lhs, other), - } -} - -pub fn exec_bit_not(reg: &OwnedValue) -> OwnedValue { - match reg { - OwnedValue::Null => OwnedValue::Null, - OwnedValue::Integer(i) => OwnedValue::Integer(!i), - OwnedValue::Float(f) => OwnedValue::Integer(!(*f as i64)), - OwnedValue::Text(text) => exec_bit_not(&cast_text_to_numeric(text.as_str())), - _ => todo!(), - } -} - -pub fn exec_shift_left(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { - match (lhs, rhs) { - (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, - (OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => { - OwnedValue::Integer(compute_shl(*lh, *rh)) - } - (OwnedValue::Float(lh), OwnedValue::Integer(rh)) => { - OwnedValue::Integer(compute_shl(*lh as i64, *rh)) - } - (OwnedValue::Integer(lh), OwnedValue::Float(rh)) => { - OwnedValue::Integer(compute_shl(*lh, *rh as i64)) - } - (OwnedValue::Float(lh), OwnedValue::Float(rh)) => { - OwnedValue::Integer(compute_shl(*lh as i64, *rh as i64)) - } - (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_shift_left( - &cast_text_to_numeric(lhs.as_str()), - &cast_text_to_numeric(rhs.as_str()), - ), - (OwnedValue::Text(text), other) => { - exec_shift_left(&cast_text_to_numeric(text.as_str()), other) - } - (other, OwnedValue::Text(text)) => { - exec_shift_left(other, &cast_text_to_numeric(text.as_str())) - } - _ => todo!(), - } -} - -fn compute_shl(lhs: i64, rhs: i64) -> i64 { - if rhs == 0 { - lhs - } else if rhs > 0 { - // for positive shifts, if it's too large return 0 - if rhs >= 64 { - 0 - } else { - lhs << rhs - } - } else { - // for negative shifts, check if it's i64::MIN to avoid overflow on negation - if rhs == i64::MIN || rhs <= -64 { - if lhs < 0 { - -1 - } else { - 0 - } - } else { - lhs >> (-rhs) - } - } -} - -pub fn exec_shift_right(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { - match (lhs, rhs) { - (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, - (OwnedValue::Integer(lh), OwnedValue::Integer(rh)) => { - OwnedValue::Integer(compute_shr(*lh, *rh)) - } - (OwnedValue::Float(lh), OwnedValue::Integer(rh)) => { - OwnedValue::Integer(compute_shr(*lh as i64, *rh)) - } - (OwnedValue::Integer(lh), OwnedValue::Float(rh)) => { - OwnedValue::Integer(compute_shr(*lh, *rh as i64)) - } - (OwnedValue::Float(lh), OwnedValue::Float(rh)) => { - OwnedValue::Integer(compute_shr(*lh as i64, *rh as i64)) - } - (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_shift_right( - &cast_text_to_numeric(lhs.as_str()), - &cast_text_to_numeric(rhs.as_str()), - ), - (OwnedValue::Text(text), other) => { - exec_shift_right(&cast_text_to_numeric(text.as_str()), other) - } - (other, OwnedValue::Text(text)) => { - exec_shift_right(other, &cast_text_to_numeric(text.as_str())) - } - _ => todo!(), - } -} - -// compute binary shift to the right if rhs >= 0 and binary shift to the left - if rhs < 0 -// note, that binary shift to the right is sign-extended -fn compute_shr(lhs: i64, rhs: i64) -> i64 { - if rhs == 0 { - lhs - } else if rhs > 0 { - // for positive right shifts - if rhs >= 64 { - if lhs < 0 { - -1 - } else { - 0 - } - } else { - lhs >> rhs - } - } else { - // for negative right shifts, check if it's i64::MIN to avoid overflow - if rhs == i64::MIN || -rhs >= 64 { - 0 - } else { - lhs << (-rhs) - } - } -} - -pub fn exec_boolean_not(reg: &OwnedValue) -> OwnedValue { - match reg { - OwnedValue::Null => OwnedValue::Null, - OwnedValue::Integer(i) => OwnedValue::Integer((*i == 0) as i64), - OwnedValue::Float(f) => OwnedValue::Integer((*f == 0.0) as i64), - OwnedValue::Text(text) => exec_boolean_not(&cast_text_to_numeric(text.as_str())), - _ => todo!(), - } -} -pub fn exec_concat(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { - match (lhs, rhs) { - (OwnedValue::Text(lhs_text), OwnedValue::Text(rhs_text)) => { - OwnedValue::build_text(&(lhs_text.as_str().to_string() + rhs_text.as_str())) - } - (OwnedValue::Text(lhs_text), OwnedValue::Integer(rhs_int)) => { - OwnedValue::build_text(&(lhs_text.as_str().to_string() + &rhs_int.to_string())) - } - (OwnedValue::Text(lhs_text), OwnedValue::Float(rhs_float)) => { - OwnedValue::build_text(&(lhs_text.as_str().to_string() + &rhs_float.to_string())) - } - (OwnedValue::Integer(lhs_int), OwnedValue::Text(rhs_text)) => { - OwnedValue::build_text(&(lhs_int.to_string() + rhs_text.as_str())) - } - (OwnedValue::Integer(lhs_int), OwnedValue::Integer(rhs_int)) => { - OwnedValue::build_text(&(lhs_int.to_string() + &rhs_int.to_string())) - } - (OwnedValue::Integer(lhs_int), OwnedValue::Float(rhs_float)) => { - OwnedValue::build_text(&(lhs_int.to_string() + &rhs_float.to_string())) - } - (OwnedValue::Float(lhs_float), OwnedValue::Text(rhs_text)) => { - OwnedValue::build_text(&(lhs_float.to_string() + rhs_text.as_str())) - } - (OwnedValue::Float(lhs_float), OwnedValue::Integer(rhs_int)) => { - OwnedValue::build_text(&(lhs_float.to_string() + &rhs_int.to_string())) - } - (OwnedValue::Float(lhs_float), OwnedValue::Float(rhs_float)) => { - OwnedValue::build_text(&(lhs_float.to_string() + &rhs_float.to_string())) - } - (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, - (OwnedValue::Blob(_), _) | (_, OwnedValue::Blob(_)) => { - todo!("TODO: Handle Blob conversion to String") - } - } -} - -pub fn exec_and(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { - match (lhs, rhs) { - (_, OwnedValue::Integer(0)) - | (OwnedValue::Integer(0), _) - | (_, OwnedValue::Float(0.0)) - | (OwnedValue::Float(0.0), _) => OwnedValue::Integer(0), - (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, - (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_and( - &cast_text_to_numeric(lhs.as_str()), - &cast_text_to_numeric(rhs.as_str()), - ), - (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { - exec_and(&cast_text_to_numeric(text.as_str()), other) - } - _ => OwnedValue::Integer(1), - } -} - -pub fn exec_or(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue { - match (lhs, rhs) { - (OwnedValue::Null, OwnedValue::Null) - | (OwnedValue::Null, OwnedValue::Float(0.0)) - | (OwnedValue::Float(0.0), OwnedValue::Null) - | (OwnedValue::Null, OwnedValue::Integer(0)) - | (OwnedValue::Integer(0), OwnedValue::Null) => OwnedValue::Null, - (OwnedValue::Float(0.0), OwnedValue::Integer(0)) - | (OwnedValue::Integer(0), OwnedValue::Float(0.0)) - | (OwnedValue::Float(0.0), OwnedValue::Float(0.0)) - | (OwnedValue::Integer(0), OwnedValue::Integer(0)) => OwnedValue::Integer(0), - (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_or( - &cast_text_to_numeric(lhs.as_str()), - &cast_text_to_numeric(rhs.as_str()), - ), - (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { - exec_or(&cast_text_to_numeric(text.as_str()), other) - } - _ => OwnedValue::Integer(1), - } -} - impl Insn { pub fn to_function(&self) -> InsnFunction { match self { @@ -1419,471 +980,19 @@ impl Insn { } } -#[cfg(test)] -mod tests { - use crate::{ - types::{OwnedValue, Text}, - vdbe::insn::exec_or, - }; - - use super::exec_add; - - #[test] - fn test_exec_add() { - let inputs = vec![ - (OwnedValue::Integer(3), OwnedValue::Integer(1)), - (OwnedValue::Float(3.0), OwnedValue::Float(1.0)), - (OwnedValue::Float(3.0), OwnedValue::Integer(1)), - (OwnedValue::Integer(3), OwnedValue::Float(1.0)), - (OwnedValue::Null, OwnedValue::Null), - (OwnedValue::Null, OwnedValue::Integer(1)), - (OwnedValue::Null, OwnedValue::Float(1.0)), - (OwnedValue::Null, OwnedValue::Text(Text::from_str("2"))), - (OwnedValue::Integer(1), OwnedValue::Null), - (OwnedValue::Float(1.0), OwnedValue::Null), - (OwnedValue::Text(Text::from_str("1")), OwnedValue::Null), - ( - OwnedValue::Text(Text::from_str("1")), - OwnedValue::Text(Text::from_str("3")), - ), - ( - OwnedValue::Text(Text::from_str("1.0")), - OwnedValue::Text(Text::from_str("3.0")), - ), - ( - OwnedValue::Text(Text::from_str("1.0")), - OwnedValue::Float(3.0), - ), - ( - OwnedValue::Text(Text::from_str("1.0")), - OwnedValue::Integer(3), - ), - ( - OwnedValue::Float(1.0), - OwnedValue::Text(Text::from_str("3.0")), - ), - ( - OwnedValue::Integer(1), - OwnedValue::Text(Text::from_str("3")), - ), - ]; - - let outputs = [ - OwnedValue::Integer(4), - OwnedValue::Float(4.0), - OwnedValue::Float(4.0), - OwnedValue::Float(4.0), - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Integer(4), - OwnedValue::Float(4.0), - OwnedValue::Float(4.0), - OwnedValue::Float(4.0), - OwnedValue::Float(4.0), - OwnedValue::Float(4.0), - ]; - - assert_eq!( - inputs.len(), - outputs.len(), - "Inputs and Outputs should have same size" - ); - for (i, (lhs, rhs)) in inputs.iter().enumerate() { - assert_eq!( - exec_add(lhs, rhs), - outputs[i], - "Wrong ADD for lhs: {}, rhs: {}", - lhs, - rhs - ); - } - } - - use super::exec_subtract; - - #[test] - fn test_exec_subtract() { - let inputs = vec![ - (OwnedValue::Integer(3), OwnedValue::Integer(1)), - (OwnedValue::Float(3.0), OwnedValue::Float(1.0)), - (OwnedValue::Float(3.0), OwnedValue::Integer(1)), - (OwnedValue::Integer(3), OwnedValue::Float(1.0)), - (OwnedValue::Null, OwnedValue::Null), - (OwnedValue::Null, OwnedValue::Integer(1)), - (OwnedValue::Null, OwnedValue::Float(1.0)), - (OwnedValue::Null, OwnedValue::Text(Text::from_str("1"))), - (OwnedValue::Integer(1), OwnedValue::Null), - (OwnedValue::Float(1.0), OwnedValue::Null), - (OwnedValue::Text(Text::from_str("4")), OwnedValue::Null), - ( - OwnedValue::Text(Text::from_str("1")), - OwnedValue::Text(Text::from_str("3")), - ), - ( - OwnedValue::Text(Text::from_str("1.0")), - OwnedValue::Text(Text::from_str("3.0")), - ), - ( - OwnedValue::Text(Text::from_str("1.0")), - OwnedValue::Float(3.0), - ), - ( - OwnedValue::Text(Text::from_str("1.0")), - OwnedValue::Integer(3), - ), - ( - OwnedValue::Float(1.0), - OwnedValue::Text(Text::from_str("3.0")), - ), - ( - OwnedValue::Integer(1), - OwnedValue::Text(Text::from_str("3")), - ), - ]; - - let outputs = [ - OwnedValue::Integer(2), - OwnedValue::Float(2.0), - OwnedValue::Float(2.0), - OwnedValue::Float(2.0), - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Integer(-2), - OwnedValue::Float(-2.0), - OwnedValue::Float(-2.0), - OwnedValue::Float(-2.0), - OwnedValue::Float(-2.0), - OwnedValue::Float(-2.0), - ]; - - assert_eq!( - inputs.len(), - outputs.len(), - "Inputs and Outputs should have same size" - ); - for (i, (lhs, rhs)) in inputs.iter().enumerate() { - assert_eq!( - exec_subtract(lhs, rhs), - outputs[i], - "Wrong subtract for lhs: {}, rhs: {}", - lhs, - rhs - ); - } - } - use super::exec_multiply; - - #[test] - fn test_exec_multiply() { - let inputs = vec![ - (OwnedValue::Integer(3), OwnedValue::Integer(2)), - (OwnedValue::Float(3.0), OwnedValue::Float(2.0)), - (OwnedValue::Float(3.0), OwnedValue::Integer(2)), - (OwnedValue::Integer(3), OwnedValue::Float(2.0)), - (OwnedValue::Null, OwnedValue::Null), - (OwnedValue::Null, OwnedValue::Integer(1)), - (OwnedValue::Null, OwnedValue::Float(1.0)), - (OwnedValue::Null, OwnedValue::Text(Text::from_str("1"))), - (OwnedValue::Integer(1), OwnedValue::Null), - (OwnedValue::Float(1.0), OwnedValue::Null), - (OwnedValue::Text(Text::from_str("4")), OwnedValue::Null), - ( - OwnedValue::Text(Text::from_str("2")), - OwnedValue::Text(Text::from_str("3")), - ), - ( - OwnedValue::Text(Text::from_str("2.0")), - OwnedValue::Text(Text::from_str("3.0")), - ), - ( - OwnedValue::Text(Text::from_str("2.0")), - OwnedValue::Float(3.0), - ), - ( - OwnedValue::Text(Text::from_str("2.0")), - OwnedValue::Integer(3), - ), - ( - OwnedValue::Float(2.0), - OwnedValue::Text(Text::from_str("3.0")), - ), - ( - OwnedValue::Integer(2), - OwnedValue::Text(Text::from_str("3.0")), - ), - ]; - - let outputs = [ - OwnedValue::Integer(6), - OwnedValue::Float(6.0), - OwnedValue::Float(6.0), - OwnedValue::Float(6.0), - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Integer(6), - OwnedValue::Float(6.0), - OwnedValue::Float(6.0), - OwnedValue::Float(6.0), - OwnedValue::Float(6.0), - OwnedValue::Float(6.0), - ]; - - assert_eq!( - inputs.len(), - outputs.len(), - "Inputs and Outputs should have same size" - ); - for (i, (lhs, rhs)) in inputs.iter().enumerate() { - assert_eq!( - exec_multiply(lhs, rhs), - outputs[i], - "Wrong multiply for lhs: {}, rhs: {}", - lhs, - rhs - ); - } - } - use super::exec_divide; - - #[test] - fn test_exec_divide() { - let inputs = vec![ - (OwnedValue::Integer(1), OwnedValue::Integer(0)), - (OwnedValue::Float(1.0), OwnedValue::Float(0.0)), - (OwnedValue::Integer(i64::MIN), OwnedValue::Integer(-1)), - (OwnedValue::Float(6.0), OwnedValue::Float(2.0)), - (OwnedValue::Float(6.0), OwnedValue::Integer(2)), - (OwnedValue::Integer(6), OwnedValue::Integer(2)), - (OwnedValue::Null, OwnedValue::Integer(2)), - (OwnedValue::Integer(2), OwnedValue::Null), - (OwnedValue::Null, OwnedValue::Null), - ( - OwnedValue::Text(Text::from_str("6")), - OwnedValue::Text(Text::from_str("2")), - ), - ( - OwnedValue::Text(Text::from_str("6")), - OwnedValue::Integer(2), - ), - ]; - - let outputs = [ - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Float(9.223372036854776e18), - OwnedValue::Float(3.0), - OwnedValue::Float(3.0), - OwnedValue::Float(3.0), - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Float(3.0), - OwnedValue::Float(3.0), - ]; - - assert_eq!( - inputs.len(), - outputs.len(), - "Inputs and Outputs should have same size" - ); - for (i, (lhs, rhs)) in inputs.iter().enumerate() { - assert_eq!( - exec_divide(lhs, rhs), - outputs[i], - "Wrong divide for lhs: {}, rhs: {}", - lhs, - rhs - ); - } - } - - use super::exec_remainder; - #[test] - fn test_exec_remainder() { - let inputs = vec![ - (OwnedValue::Null, OwnedValue::Null), - (OwnedValue::Null, OwnedValue::Float(1.0)), - (OwnedValue::Null, OwnedValue::Integer(1)), - (OwnedValue::Null, OwnedValue::Text(Text::from_str("1"))), - (OwnedValue::Float(1.0), OwnedValue::Null), - (OwnedValue::Integer(1), OwnedValue::Null), - (OwnedValue::Integer(12), OwnedValue::Integer(0)), - (OwnedValue::Float(12.0), OwnedValue::Float(0.0)), - (OwnedValue::Float(12.0), OwnedValue::Integer(0)), - (OwnedValue::Integer(12), OwnedValue::Float(0.0)), - (OwnedValue::Integer(i64::MIN), OwnedValue::Integer(-1)), - (OwnedValue::Integer(12), OwnedValue::Integer(3)), - (OwnedValue::Float(12.0), OwnedValue::Float(3.0)), - (OwnedValue::Float(12.0), OwnedValue::Integer(3)), - (OwnedValue::Integer(12), OwnedValue::Float(3.0)), - (OwnedValue::Integer(12), OwnedValue::Integer(-3)), - (OwnedValue::Float(12.0), OwnedValue::Float(-3.0)), - (OwnedValue::Float(12.0), OwnedValue::Integer(-3)), - (OwnedValue::Integer(12), OwnedValue::Float(-3.0)), - ( - OwnedValue::Text(Text::from_str("12.0")), - OwnedValue::Text(Text::from_str("3.0")), - ), - ( - OwnedValue::Text(Text::from_str("12.0")), - OwnedValue::Float(3.0), - ), - ( - OwnedValue::Float(12.0), - OwnedValue::Text(Text::from_str("3.0")), - ), - ]; - let outputs = vec![ - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Float(0.0), - OwnedValue::Integer(0), - OwnedValue::Float(0.0), - OwnedValue::Float(0.0), - OwnedValue::Float(0.0), - OwnedValue::Integer(0), - OwnedValue::Float(0.0), - OwnedValue::Float(0.0), - OwnedValue::Float(0.0), - OwnedValue::Float(0.0), - OwnedValue::Float(0.0), - OwnedValue::Float(0.0), - ]; - - assert_eq!( - inputs.len(), - outputs.len(), - "Inputs and Outputs should have same size" - ); - - for (i, (lhs, rhs)) in inputs.iter().enumerate() { - assert_eq!( - exec_remainder(lhs, rhs), - outputs[i], - "Wrong remainder for lhs: {}, rhs: {}", - lhs, - rhs - ); - } - } - - use super::exec_and; - - #[test] - fn test_exec_and() { - let inputs = vec![ - (OwnedValue::Integer(0), OwnedValue::Null), - (OwnedValue::Null, OwnedValue::Integer(1)), - (OwnedValue::Null, OwnedValue::Null), - (OwnedValue::Float(0.0), OwnedValue::Null), - (OwnedValue::Integer(1), OwnedValue::Float(2.2)), - ( - OwnedValue::Integer(0), - OwnedValue::Text(Text::from_str("string")), - ), - ( - OwnedValue::Integer(0), - OwnedValue::Text(Text::from_str("1")), - ), - ( - OwnedValue::Integer(1), - OwnedValue::Text(Text::from_str("1")), - ), - ]; - let outputs = [ - OwnedValue::Integer(0), - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Integer(0), - OwnedValue::Integer(1), - OwnedValue::Integer(0), - OwnedValue::Integer(0), - OwnedValue::Integer(1), - ]; - - assert_eq!( - inputs.len(), - outputs.len(), - "Inputs and Outputs should have same size" - ); - for (i, (lhs, rhs)) in inputs.iter().enumerate() { - assert_eq!( - exec_and(lhs, rhs), - outputs[i], - "Wrong AND for lhs: {}, rhs: {}", - lhs, - rhs - ); - } - } - - #[test] - fn test_exec_or() { - let inputs = vec![ - (OwnedValue::Integer(0), OwnedValue::Null), - (OwnedValue::Null, OwnedValue::Integer(1)), - (OwnedValue::Null, OwnedValue::Null), - (OwnedValue::Float(0.0), OwnedValue::Null), - (OwnedValue::Integer(1), OwnedValue::Float(2.2)), - (OwnedValue::Float(0.0), OwnedValue::Integer(0)), - ( - OwnedValue::Integer(0), - OwnedValue::Text(Text::from_str("string")), - ), - ( - OwnedValue::Integer(0), - OwnedValue::Text(Text::from_str("1")), - ), - (OwnedValue::Integer(0), OwnedValue::Text(Text::from_str(""))), - ]; - let outputs = [ - OwnedValue::Null, - OwnedValue::Integer(1), - OwnedValue::Null, - OwnedValue::Null, - OwnedValue::Integer(1), - OwnedValue::Integer(0), - OwnedValue::Integer(0), - OwnedValue::Integer(1), - OwnedValue::Integer(0), - ]; - - assert_eq!( - inputs.len(), - outputs.len(), - "Inputs and Outputs should have same size" - ); - for (i, (lhs, rhs)) in inputs.iter().enumerate() { - assert_eq!( - exec_or(lhs, rhs), - outputs[i], - "Wrong OR for lhs: {}, rhs: {}", - lhs, - rhs - ); - } - } +// TODO: Add remaining cookies. +#[derive(Description, Debug, Clone, Copy)] +pub enum Cookie { + /// The schema cookie. + SchemaVersion = 1, + /// The schema format number. Supported schema formats are 1, 2, 3, and 4. + DatabaseFormat = 2, + /// Default page cache size. + DefaultPageCacheSize = 3, + /// The page number of the largest root b-tree page when in auto-vacuum or incremental-vacuum modes, or zero otherwise. + LargestRootPageNumber = 4, + /// The database text encoding. A value of 1 means UTF-8. A value of 2 means UTF-16le. A value of 3 means UTF-16be. + DatabaseTextEncoding = 5, + /// The "user version" as read and set by the user_version pragma. + UserVersion = 6, } diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index cf6918304..daad191b4 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -24,19 +24,18 @@ pub mod insn; pub mod likeop; pub mod sorter; -use crate::error::LimboError; -use crate::fast_lock::SpinLock; -use crate::function::{AggFunc, FuncCtx}; - -use crate::storage::sqlite3_ondisk::DatabaseHeader; -use crate::storage::{btree::BTreeCursor, pager::Pager}; -use crate::translate::plan::{ResultSetColumn, TableReference}; -use crate::types::{ - AggContext, Cursor, CursorResult, ImmutableRecord, OwnedValue, SeekKey, SeekOp, +use crate::{ + error::LimboError, + fast_lock::SpinLock, + function::{AggFunc, FuncCtx}, +}; + +use crate::{ + storage::{btree::BTreeCursor, pager::Pager, sqlite3_ondisk::DatabaseHeader}, + translate::plan::{ResultSetColumn, TableReference}, + types::{AggContext, Cursor, CursorResult, ImmutableRecord, OwnedValue, SeekKey, SeekOp}, + vdbe::{builder::CursorType, insn::Insn}, }; -use crate::util::cast_text_to_numeric; -use crate::vdbe::builder::CursorType; -use crate::vdbe::insn::Insn; use crate::CheckpointStatus; @@ -45,16 +44,20 @@ use crate::json::JsonCacheCell; use crate::{Connection, MvStore, Result, TransactionState}; use execute::{InsnFunction, InsnFunctionStepResult}; -use rand::distributions::{Distribution, Uniform}; -use rand::Rng; +use rand::{ + distributions::{Distribution, Uniform}, + Rng, +}; use regex::Regex; -use std::cell::{Cell, RefCell}; -use std::collections::HashMap; -use std::ffi::c_void; -use std::num::NonZero; -use std::ops::Deref; -use std::rc::{Rc, Weak}; -use std::sync::Arc; +use std::{ + cell::{Cell, RefCell}, + collections::HashMap, + ffi::c_void, + num::NonZero, + ops::Deref, + rc::{Rc, Weak}, + sync::Arc, +}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] /// Represents a target for a jump instruction.