use std::sync::Arc;
use crate::common::SmartString;
use crate::common::CompactArc;
use super::compiler::{CompileContext, ExprCompiler};
use super::ops::{CompiledPattern, Op};
use super::program::Program;
use super::vm::{ExecuteContext, ExprVM};
use crate::core::{Value, ValueSet};
use crate::Row;
#[test]
fn test_simple_load_and_compare() {
let mut vm = ExprVM::new();
let program = Program::new(vec![
Op::LoadColumn(0),
Op::LoadConst(Value::Integer(5)),
Op::Gt,
Op::Return,
]);
let row = Row::from_values(vec![Value::Integer(10)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(true));
let row = Row::from_values(vec![Value::Integer(3)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(false));
}
#[test]
fn test_null_comparison() {
let mut vm = ExprVM::new();
let program = Program::new(vec![
Op::LoadColumn(0),
Op::LoadConst(Value::Integer(5)),
Op::Gt,
Op::Return,
]);
let row = Row::from_values(vec![Value::Null(crate::core::DataType::Integer)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert!(result.is_null());
}
#[test]
fn test_and_short_circuit() {
let mut vm = ExprVM::new();
let program = Program::new(vec![
Op::LoadColumn(0),
Op::LoadConst(Value::Integer(5)),
Op::Gt,
Op::And(10), Op::LoadColumn(1),
Op::LoadConst(Value::Integer(10)),
Op::Lt,
Op::AndFinalize,
Op::Return,
Op::Nop, Op::LoadConst(Value::Boolean(false)), Op::Return,
]);
let row = Row::from_values(vec![Value::Integer(10), Value::Integer(5)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(true));
let row = Row::from_values(vec![Value::Integer(3), Value::Integer(5)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(false));
let row = Row::from_values(vec![Value::Integer(10), Value::Integer(15)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(false));
}
#[test]
fn test_or_short_circuit() {
let mut vm = ExprVM::new();
let program = Program::new(vec![
Op::LoadColumn(0),
Op::LoadConst(Value::Integer(5)),
Op::Lt,
Op::Or(10), Op::LoadColumn(1),
Op::LoadConst(Value::Integer(10)),
Op::Gt,
Op::OrFinalize,
Op::Return,
Op::Nop, Op::LoadConst(Value::Boolean(true)), Op::Return,
]);
let row = Row::from_values(vec![Value::Integer(3), Value::Integer(5)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(true));
let row = Row::from_values(vec![Value::Integer(10), Value::Integer(15)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(true));
let row = Row::from_values(vec![Value::Integer(10), Value::Integer(5)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(false));
}
#[test]
fn test_arithmetic() {
let mut vm = ExprVM::new();
let program = Program::new(vec![
Op::LoadColumn(0),
Op::LoadColumn(1),
Op::LoadConst(Value::Integer(2)),
Op::Mul,
Op::Add,
Op::Return,
]);
let row = Row::from_values(vec![Value::Integer(5), Value::Integer(3)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Integer(11)); }
#[test]
fn test_in_set() {
let mut vm = ExprVM::new();
let set: ValueSet = [Value::Integer(1), Value::Integer(2), Value::Integer(3)]
.into_iter()
.collect();
let program = Program::new(vec![
Op::LoadColumn(0),
Op::InSet(CompactArc::new(set), false),
Op::Return,
]);
let row = Row::from_values(vec![Value::Integer(2)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(true));
let row = Row::from_values(vec![Value::Integer(5)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(false));
}
#[test]
fn test_between() {
let mut vm = ExprVM::new();
let program = Program::new(vec![
Op::LoadColumn(0),
Op::LoadConst(Value::Integer(5)),
Op::LoadConst(Value::Integer(10)),
Op::Between,
Op::Return,
]);
let row = Row::from_values(vec![Value::Integer(7)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(true));
let row = Row::from_values(vec![Value::Integer(3)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(false));
let row = Row::from_values(vec![Value::Integer(15)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(false));
}
#[test]
fn test_like_pattern() {
let mut vm = ExprVM::new();
let pattern = CompiledPattern::compile("test%", false);
let program = Program::new(vec![
Op::LoadColumn(0),
Op::Like(Arc::new(pattern), false),
Op::Return,
]);
let row = Row::from_values(vec![Value::Text(SmartString::from("testing"))]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(true));
let row = Row::from_values(vec![Value::Text(SmartString::from("other"))]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(false));
}
#[test]
fn test_is_null() {
let mut vm = ExprVM::new();
let program = Program::new(vec![Op::LoadColumn(0), Op::IsNull, Op::Return]);
let row = Row::from_values(vec![Value::Null(crate::core::DataType::Integer)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(true));
let row = Row::from_values(vec![Value::Integer(5)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(false));
}
#[test]
fn test_coalesce() {
let mut vm = ExprVM::new();
let program = Program::new(vec![
Op::LoadColumn(0),
Op::LoadColumn(1),
Op::LoadConst(Value::Text(SmartString::from("default"))),
Op::Coalesce(3),
Op::Return,
]);
let row = Row::from_values(vec![
Value::Text(SmartString::from("first")),
Value::Text(SmartString::from("second")),
]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Text(SmartString::from("first")));
let row = Row::from_values(vec![
Value::Null(crate::core::DataType::Text),
Value::Text(SmartString::from("second")),
]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Text(SmartString::from("second")));
let row = Row::from_values(vec![
Value::Null(crate::core::DataType::Text),
Value::Null(crate::core::DataType::Text),
]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Text(SmartString::from("default")));
}
#[test]
fn test_join_context() {
let mut vm = ExprVM::new();
let program = Program::new(vec![
Op::LoadColumn(0), Op::LoadColumn2(0), Op::Eq,
Op::Return,
]);
let row1 = Row::from_values(vec![Value::Integer(5)]);
let row2 = Row::from_values(vec![Value::Integer(5)]);
let ctx = ExecuteContext::for_join(&row1, &row2);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(true));
let row1 = Row::from_values(vec![Value::Integer(5)]);
let row2 = Row::from_values(vec![Value::Integer(10)]);
let ctx = ExecuteContext::for_join(&row1, &row2);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(false));
}
#[test]
fn test_parameters() {
let mut vm = ExprVM::new();
let program = Program::new(vec![
Op::LoadColumn(0),
Op::LoadParam(0),
Op::Eq,
Op::Return,
]);
let row = Row::from_values(vec![Value::Integer(42)]);
let params = vec![Value::Integer(42)];
let ctx = ExecuteContext::new(&row).with_params(¶ms);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(true));
let params = vec![Value::Integer(100)];
let ctx = ExecuteContext::new(&row).with_params(¶ms);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(false));
}
#[test]
fn test_execute_bool() {
let mut vm = ExprVM::new();
let program = Program::new(vec![
Op::LoadColumn(0),
Op::LoadConst(Value::Integer(5)),
Op::Gt,
Op::Return,
]);
let row = Row::from_values(vec![Value::Integer(10)]);
let ctx = ExecuteContext::new(&row);
assert!(vm.execute_bool(&program, &ctx));
let row = Row::from_values(vec![Value::Integer(3)]);
let ctx = ExecuteContext::new(&row);
assert!(!vm.execute_bool(&program, &ctx));
let row = Row::from_values(vec![Value::Null(crate::core::DataType::Integer)]);
let ctx = ExecuteContext::new(&row);
assert!(!vm.execute_bool(&program, &ctx));
}
#[test]
fn test_compiler_simple_expression() {
use crate::parser::ast::*;
use crate::parser::token::{Position, Token, TokenType};
let columns = vec!["a".to_string(), "b".to_string()];
let ctx = CompileContext::with_global_registry(&columns);
let compiler = ExprCompiler::new(&ctx);
fn make_token() -> Token {
Token {
token_type: TokenType::Integer,
literal: "1".into(),
position: Position {
offset: 0,
line: 1,
column: 1,
},
quoted: false,
}
}
let expr = Expression::Infix(InfixExpression {
token: make_token(),
left: Box::new(Expression::Identifier(Identifier::new(
make_token(),
"a".to_string(),
))),
operator: ">".into(),
op_type: InfixOperator::GreaterThan,
right: Box::new(Expression::IntegerLiteral(IntegerLiteral {
token: make_token(),
value: 5,
})),
});
let program = compiler.compile(&expr).unwrap();
let mut vm = ExprVM::new();
let row = Row::from_values(vec![Value::Integer(10), Value::Integer(20)]);
let ctx = ExecuteContext::new(&row);
let result = vm.execute(&program, &ctx).unwrap();
assert_eq!(result, Value::Boolean(true));
}
#[test]
fn test_compiled_pattern_prefix() {
let pattern = CompiledPattern::compile("test%", false);
assert!(pattern.matches("testing", false));
assert!(pattern.matches("test", false));
assert!(!pattern.matches("atest", false));
}
#[test]
fn test_compiled_pattern_suffix() {
let pattern = CompiledPattern::compile("%test", false);
assert!(pattern.matches("mytest", false));
assert!(pattern.matches("test", false));
assert!(!pattern.matches("testa", false));
}
#[test]
fn test_compiled_pattern_contains() {
let pattern = CompiledPattern::compile("%test%", false);
assert!(pattern.matches("mytesting", false));
assert!(pattern.matches("test", false));
assert!(pattern.matches("atest", false));
assert!(!pattern.matches("other", false));
}
#[test]
fn test_compiled_pattern_case_insensitive() {
let pattern = CompiledPattern::compile("TEST%", true);
assert!(pattern.matches("testing", true));
assert!(pattern.matches("TESTING", true));
assert!(pattern.matches("TeStInG", true));
}