Merge 'VDBE code cleanups' from Pekka Enberg

Closes #1291
This commit is contained in:
Pekka Enberg
2025-04-10 12:10:22 +03:00
3 changed files with 961 additions and 960 deletions

View File

@@ -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;

View File

@@ -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,
}

View File

@@ -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.