mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-03 17:48:17 +00:00
core/vdbe: Move exec_*()
funtions to execute.rs
This commit is contained in:
parent
3fd51cdf06
commit
31f0d174d7
3 changed files with 884 additions and 901 deletions
|
@ -39,11 +39,7 @@ use crate::{
|
|||
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,
|
||||
},
|
||||
insn::{Cookie, RegisterOrLiteral},
|
||||
HaltState,
|
||||
};
|
||||
use rand::thread_rng;
|
||||
|
@ -5368,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,
|
||||
|
@ -5379,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;
|
||||
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
use std::{num::NonZero, rc::Rc};
|
||||
|
||||
use super::{
|
||||
cast_text_to_numeric, execute, AggFunc, BranchOffset, CursorID, FuncCtx, InsnFunction, PageIdx,
|
||||
};
|
||||
use crate::{
|
||||
schema::BTreeTable,
|
||||
storage::wal::CheckpointMode,
|
||||
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.
|
||||
|
@ -1002,889 +996,3 @@ pub enum Cookie {
|
|||
/// 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),
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ use crate::{
|
|||
storage::{btree::BTreeCursor, pager::Pager, sqlite3_ondisk::DatabaseHeader},
|
||||
translate::plan::{ResultSetColumn, TableReference},
|
||||
types::{AggContext, Cursor, CursorResult, ImmutableRecord, OwnedValue, SeekKey, SeekOp},
|
||||
util::cast_text_to_numeric,
|
||||
vdbe::{builder::CursorType, insn::Insn},
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue