use std::hash::{Hash, Hasher};
use std::num::NonZeroUsize;
use std::sync::Arc;
use crate::api::params::ParamVec;
use crate::common::{CompactArc, StringMap};
use lru::LruCache;
use parking_lot::Mutex;
use rustc_hash::{FxHashMap, FxHasher};
use super::compiler::{CompileContext, ExprCompiler};
use super::program::Program;
use super::vm::{ExecuteContext, ExprVM};
use crate::core::{Error, Result, Row, Value};
use crate::functions::{global_registry, FunctionRegistry};
use crate::parser::ast::Expression;
use crate::executor::context::ExecutionContext;
const PROGRAM_CACHE_SIZE: usize = 256;
static PROGRAM_CACHE: Mutex<Option<LruCache<u64, SharedProgram>>> = Mutex::new(None);
pub fn clear_program_cache() {
let mut guard = PROGRAM_CACHE.lock();
*guard = None;
}
fn compute_cache_key(expr: &Expression, columns: &[String]) -> u64 {
let mut hasher = FxHasher::default();
hash_expression(expr, &mut hasher);
columns.hash(&mut hasher);
hasher.finish()
}
#[inline]
pub fn compute_expression_hash(expr: &Expression) -> u64 {
let mut hasher = FxHasher::default();
hash_expression(expr, &mut hasher);
hasher.finish()
}
fn hash_expression(expr: &Expression, hasher: &mut FxHasher) {
std::mem::discriminant(expr).hash(hasher);
match expr {
Expression::Identifier(id) => {
id.value_lower.hash(hasher);
}
Expression::QualifiedIdentifier(qid) => {
qid.qualifier.value_lower.hash(hasher);
qid.name.value_lower.hash(hasher);
}
Expression::IntegerLiteral(lit) => {
lit.value.hash(hasher);
}
Expression::FloatLiteral(lit) => {
lit.value.to_bits().hash(hasher);
}
Expression::StringLiteral(lit) => {
lit.value.hash(hasher);
lit.type_hint.hash(hasher);
}
Expression::BooleanLiteral(lit) => {
lit.value.hash(hasher);
}
Expression::NullLiteral(_) => {
}
Expression::IntervalLiteral(lit) => {
lit.value.hash(hasher);
lit.unit.hash(hasher);
}
Expression::Parameter(param) => {
param.index.hash(hasher);
param.name.hash(hasher);
}
Expression::Prefix(prefix) => {
std::mem::discriminant(&prefix.op_type).hash(hasher);
hash_expression(&prefix.right, hasher);
}
Expression::Infix(infix) => {
std::mem::discriminant(&infix.op_type).hash(hasher);
hash_expression(&infix.left, hasher);
hash_expression(&infix.right, hasher);
}
Expression::List(list) => {
list.elements.len().hash(hasher);
for val in &list.elements {
hash_expression(val, hasher);
}
}
Expression::Distinct(dist) => {
hash_expression(&dist.expr, hasher);
}
Expression::Exists(exists) => {
(exists.subquery.as_ref() as *const _ as usize).hash(hasher);
}
Expression::AllAny(aa) => {
aa.operator.hash(hasher);
std::mem::discriminant(&aa.all_any_type).hash(hasher);
hash_expression(&aa.left, hasher);
(aa.subquery.as_ref() as *const _ as usize).hash(hasher);
}
Expression::In(in_expr) => {
in_expr.not.hash(hasher);
hash_expression(&in_expr.left, hasher);
hash_expression(&in_expr.right, hasher);
}
Expression::InHashSet(in_hash) => {
in_hash.not.hash(hasher);
hash_expression(&in_hash.column, hasher);
in_hash.values.len().hash(hasher);
}
Expression::Between(between) => {
between.not.hash(hasher);
hash_expression(&between.expr, hasher);
hash_expression(&between.lower, hasher);
hash_expression(&between.upper, hasher);
}
Expression::Like(like) => {
like.operator.hash(hasher);
hash_expression(&like.left, hasher);
hash_expression(&like.pattern, hasher);
if let Some(ref escape) = like.escape {
true.hash(hasher);
hash_expression(escape, hasher);
} else {
false.hash(hasher);
}
}
Expression::ScalarSubquery(sq) => {
(sq.subquery.as_ref() as *const _ as usize).hash(hasher);
}
Expression::ExpressionList(list) => {
list.expressions.len().hash(hasher);
for e in &list.expressions {
hash_expression(e, hasher);
}
}
Expression::Case(case) => {
if let Some(ref val) = case.value {
true.hash(hasher);
hash_expression(val, hasher);
} else {
false.hash(hasher);
}
case.when_clauses.len().hash(hasher);
for when_clause in &case.when_clauses {
hash_expression(&when_clause.condition, hasher);
hash_expression(&when_clause.then_result, hasher);
}
if let Some(ref else_val) = case.else_value {
true.hash(hasher);
hash_expression(else_val, hasher);
} else {
false.hash(hasher);
}
}
Expression::Cast(cast) => {
hash_expression(&cast.expr, hasher);
cast.type_name.hash(hasher);
}
Expression::FunctionCall(func) => {
func.function.hash(hasher);
func.is_distinct.hash(hasher);
func.arguments.len().hash(hasher);
for arg in &func.arguments {
hash_expression(arg, hasher);
}
if let Some(ref filter) = func.filter {
true.hash(hasher);
hash_expression(filter, hasher);
} else {
false.hash(hasher);
}
}
Expression::Aliased(aliased) => {
aliased.alias.value_lower.hash(hasher);
hash_expression(&aliased.expression, hasher);
}
Expression::Window(window) => {
window.function.function.hash(hasher);
window.function.is_distinct.hash(hasher);
window.function.arguments.len().hash(hasher);
for arg in &window.function.arguments {
hash_expression(arg, hasher);
}
window.partition_by.len().hash(hasher);
for e in &window.partition_by {
hash_expression(e, hasher);
}
window.order_by.len().hash(hasher);
for order in &window.order_by {
hash_expression(&order.expression, hasher);
order.ascending.hash(hasher);
order.nulls_first.hash(hasher);
}
}
Expression::TableSource(ts) => {
ts.name.value_lower.hash(hasher);
if let Some(ref alias) = ts.alias {
true.hash(hasher);
alias.value_lower.hash(hasher);
} else {
false.hash(hasher);
}
}
Expression::JoinSource(js) => {
(js.as_ref() as *const _ as usize).hash(hasher);
}
Expression::SubquerySource(sq) => {
if let Some(ref alias) = sq.alias {
true.hash(hasher);
alias.value_lower.hash(hasher);
} else {
false.hash(hasher);
}
(sq.subquery.as_ref() as *const _ as usize).hash(hasher);
}
Expression::ValuesSource(vs) => {
if let Some(ref alias) = vs.alias {
true.hash(hasher);
alias.value_lower.hash(hasher);
} else {
false.hash(hasher);
}
vs.rows.len().hash(hasher);
}
Expression::CteReference(cte) => {
cte.name.value_lower.hash(hasher);
}
Expression::FunctionTableSource(fts) => {
fts.function.value_lower.hash(hasher);
for arg in &fts.arguments {
hash_expression(arg, hasher);
}
}
Expression::Star(_) => {
}
Expression::QualifiedStar(qs) => {
qs.qualifier.hash(hasher);
}
Expression::Default(_) => {
}
}
}
fn compile_expression_cached(expr: &Expression, columns: &[String]) -> Result<SharedProgram> {
let cache_key = compute_cache_key(expr, columns);
{
let mut guard = PROGRAM_CACHE.lock();
let cache = guard.get_or_insert_with(|| {
LruCache::new(NonZeroUsize::new(PROGRAM_CACHE_SIZE).unwrap())
});
if let Some(program) = cache.get(&cache_key) {
return Ok(program.clone());
}
}
let ctx = CompileContext::with_global_registry(columns);
let compiler = ExprCompiler::new(&ctx);
let program: SharedProgram = compiler
.compile(expr)
.map(CompactArc::new)
.map_err(|e| Error::internal(format!("Compile error: {}", e)))?;
{
let mut guard = PROGRAM_CACHE.lock();
let cache = guard
.get_or_insert_with(|| LruCache::new(NonZeroUsize::new(PROGRAM_CACHE_SIZE).unwrap()));
cache.put(cache_key, program.clone());
}
Ok(program)
}
pub fn compile_expression(expr: &Expression, columns: &[String]) -> Result<SharedProgram> {
compile_expression_cached(expr, columns)
}
pub fn try_eval_constant_expr(expr: &Expression) -> Option<Value> {
use std::cell::RefCell;
if contains_context_dependent_function(expr) {
return None;
}
thread_local! {
static EVAL_VM: RefCell<ExprVM> = RefCell::new(ExprVM::new());
static EVAL_ROW: Row = Row::new();
}
let empty_cols: &[String] = &[];
let ctx = CompileContext::with_global_registry(empty_cols);
let compiler = ExprCompiler::new(&ctx);
let program = compiler.compile(expr).ok()?;
EVAL_ROW.with(|empty_row| {
let exec_ctx = ExecuteContext::new(empty_row);
EVAL_VM.with(|vm_cell| {
let mut vm = vm_cell.borrow_mut();
vm.execute(&program, &exec_ctx).ok()
})
})
}
fn contains_context_dependent_function(expr: &Expression) -> bool {
match expr {
Expression::FunctionCall(func) => {
func.function.eq_ignore_ascii_case("CURRENT_TRANSACTION_ID")
|| func
.arguments
.iter()
.any(contains_context_dependent_function)
}
Expression::Infix(infix) => {
contains_context_dependent_function(&infix.left)
|| contains_context_dependent_function(&infix.right)
}
Expression::Prefix(prefix) => contains_context_dependent_function(&prefix.right),
Expression::Cast(cast) => contains_context_dependent_function(&cast.expr),
Expression::Case(case) => {
case.value
.as_ref()
.is_some_and(|v| contains_context_dependent_function(v))
|| case.when_clauses.iter().any(|w| {
contains_context_dependent_function(&w.condition)
|| contains_context_dependent_function(&w.then_result)
})
|| case
.else_value
.as_ref()
.is_some_and(|v| contains_context_dependent_function(v))
}
Expression::Between(between) => {
contains_context_dependent_function(&between.expr)
|| contains_context_dependent_function(&between.lower)
|| contains_context_dependent_function(&between.upper)
}
Expression::In(in_expr) => {
contains_context_dependent_function(&in_expr.left)
|| contains_context_dependent_function(&in_expr.right)
}
Expression::Like(like) => {
contains_context_dependent_function(&like.left)
|| contains_context_dependent_function(&like.pattern)
|| like
.escape
.as_ref()
.is_some_and(|e| contains_context_dependent_function(e))
}
Expression::List(list) => list
.elements
.iter()
.any(contains_context_dependent_function),
Expression::ExpressionList(list) => list
.expressions
.iter()
.any(contains_context_dependent_function),
Expression::Aliased(aliased) => contains_context_dependent_function(&aliased.expression),
Expression::Distinct(distinct) => contains_context_dependent_function(&distinct.expr),
Expression::AllAny(all_any) => contains_context_dependent_function(&all_any.left),
Expression::InHashSet(in_hash) => contains_context_dependent_function(&in_hash.column),
_ => false,
}
}
pub fn compile_expression_with_context(
expr: &Expression,
columns: &[String],
outer_columns: Option<&[String]>,
function_registry: &FunctionRegistry,
) -> Result<SharedProgram> {
let mut ctx = CompileContext::new(columns, function_registry);
if let Some(outer_cols) = outer_columns {
ctx = ctx.with_outer_columns(outer_cols);
}
let compiler = ExprCompiler::new(&ctx);
compiler
.compile(expr)
.map(CompactArc::new)
.map_err(|e| Error::internal(format!("Compile error: {}", e)))
}
#[derive(Clone)]
pub struct RowFilter {
program: SharedProgram,
params: CompactArc<ParamVec>,
named_params: Arc<FxHashMap<String, Value>>,
transaction_id: Option<u64>,
}
impl RowFilter {
pub fn new(expr: &Expression, columns: &[String]) -> Result<Self> {
let program = compile_expression(expr, columns)?;
Ok(Self {
program,
params: CompactArc::new(ParamVec::new()),
named_params: Arc::new(FxHashMap::default()),
transaction_id: None,
})
}
pub fn with_aliases(
expr: &Expression,
columns: &[String],
aliases: &[(String, usize)],
) -> Result<Self> {
let alias_map: StringMap<u16> = aliases
.iter()
.map(|(name, idx)| (name.to_lowercase(), *idx as u16))
.collect();
let ctx = CompileContext::with_global_registry(columns).with_expression_aliases(alias_map);
let compiler = ExprCompiler::new(&ctx);
let program = compiler
.compile(expr)
.map(CompactArc::new)
.map_err(|e| Error::internal(format!("Compile error: {}", e)))?;
Ok(Self {
program,
params: CompactArc::new(ParamVec::new()),
named_params: Arc::new(FxHashMap::default()),
transaction_id: None,
})
}
pub fn with_params(mut self, params: ParamVec) -> Self {
self.params = CompactArc::new(params);
self
}
pub fn with_named_params(mut self, named_params: FxHashMap<String, Value>) -> Self {
self.named_params = Arc::new(named_params);
self
}
pub fn with_context(mut self, ctx: &ExecutionContext) -> Self {
self.params = CompactArc::clone(ctx.params_arc());
self.named_params = Arc::clone(ctx.named_params_arc());
self.transaction_id = ctx.transaction_id();
self
}
pub fn from_program(program: SharedProgram) -> Self {
Self {
program,
params: CompactArc::new(ParamVec::new()),
named_params: Arc::new(FxHashMap::default()),
transaction_id: None,
}
}
#[inline]
pub fn matches(&self, row: &Row) -> bool {
thread_local! {
static VM: std::cell::RefCell<ExprVM> = std::cell::RefCell::new(ExprVM::new());
}
VM.with(|vm| {
let mut ctx = ExecuteContext::new(row);
if !self.params.is_empty() {
ctx = ctx.with_params(&self.params);
}
if !self.named_params.is_empty() {
ctx = ctx.with_named_params(&self.named_params);
}
ctx = ctx.with_transaction_id(self.transaction_id);
if let Ok(mut borrowed_vm) = vm.try_borrow_mut() {
borrowed_vm.execute_bool(&self.program, &ctx)
} else {
let mut temp_vm = ExprVM::new();
temp_vm.execute_bool(&self.program, &ctx)
}
})
}
#[inline]
pub fn matches_checked(&self, row: &Row) -> Result<bool> {
thread_local! {
static VM: std::cell::RefCell<ExprVM> = std::cell::RefCell::new(ExprVM::new());
}
VM.with(|vm| {
let mut ctx = ExecuteContext::new(row);
if !self.params.is_empty() {
ctx = ctx.with_params(&self.params);
}
if !self.named_params.is_empty() {
ctx = ctx.with_named_params(&self.named_params);
}
ctx = ctx.with_transaction_id(self.transaction_id);
if let Ok(mut borrowed_vm) = vm.try_borrow_mut() {
borrowed_vm.execute_bool_checked(&self.program, &ctx)
} else {
let mut temp_vm = ExprVM::new();
temp_vm.execute_bool_checked(&self.program, &ctx)
}
})
}
pub fn retain_checked(&self, rows: &mut crate::core::RowVec) -> Result<()> {
let mut error: Option<crate::core::Error> = None;
rows.retain(|(_, row)| {
if error.is_some() {
return false;
}
match self.matches_checked(row) {
Ok(b) => b,
Err(e) => {
error = Some(e);
false
}
}
});
match error {
Some(e) => Err(e),
None => Ok(()),
}
}
#[inline]
pub fn evaluate(&self, row: &Row) -> Result<Value> {
thread_local! {
static VM: std::cell::RefCell<ExprVM> = std::cell::RefCell::new(ExprVM::new());
}
VM.with(|vm| {
let mut ctx = ExecuteContext::new(row);
if !self.params.is_empty() {
ctx = ctx.with_params(&self.params);
}
if !self.named_params.is_empty() {
ctx = ctx.with_named_params(&self.named_params);
}
ctx = ctx.with_transaction_id(self.transaction_id);
if let Ok(mut borrowed_vm) = vm.try_borrow_mut() {
borrowed_vm.execute_cow(&self.program, &ctx)
} else {
let mut temp_vm = ExprVM::new();
temp_vm.execute_cow(&self.program, &ctx)
}
})
}
pub fn program(&self) -> &SharedProgram {
&self.program
}
}
const _: () = {
const fn assert_send_sync<T: Send + Sync>() {}
let _ = assert_send_sync::<RowFilter>;
};
#[derive(Clone)]
pub struct JoinFilter {
program: SharedProgram,
params: CompactArc<ParamVec>,
named_params: Arc<FxHashMap<String, Value>>,
}
impl JoinFilter {
pub fn new(
expr: &Expression,
left_columns: &[String],
right_columns: &[String],
function_registry: &FunctionRegistry,
) -> Result<Self> {
let ctx =
CompileContext::new(left_columns, function_registry).with_second_row(right_columns);
let compiler = ExprCompiler::new(&ctx);
let program = compiler
.compile(expr)
.map_err(|e| Error::internal(format!("Compile error: {}", e)))?;
Ok(Self {
program: CompactArc::new(program),
params: CompactArc::new(ParamVec::new()),
named_params: Arc::new(FxHashMap::default()),
})
}
#[inline]
pub fn with_context(mut self, ctx: &ExecutionContext) -> Self {
self.params = CompactArc::clone(ctx.params_arc());
self.named_params = Arc::clone(ctx.named_params_arc());
self
}
#[inline]
pub fn matches(&self, left_row: &Row, right_row: &Row) -> bool {
thread_local! {
static VM: std::cell::RefCell<ExprVM> = std::cell::RefCell::new(ExprVM::new());
}
VM.with(|vm| {
let mut ctx = ExecuteContext::for_join(left_row, right_row);
if !self.params.is_empty() {
ctx = ctx.with_params(&self.params);
}
if !self.named_params.is_empty() {
ctx = ctx.with_named_params(&self.named_params);
}
if let Ok(mut borrowed_vm) = vm.try_borrow_mut() {
borrowed_vm.execute_bool(&self.program, &ctx)
} else {
let mut temp_vm = ExprVM::new();
temp_vm.execute_bool(&self.program, &ctx)
}
})
}
pub fn program(&self) -> &SharedProgram {
&self.program
}
}
const _: () = {
const fn assert_send_sync<T: Send + Sync>() {}
let _ = assert_send_sync::<JoinFilter>;
};
pub struct ExpressionEval {
program: SharedProgram,
vm: ExprVM,
params: CompactArc<ParamVec>,
named_params: Arc<FxHashMap<String, Value>>,
outer_row: Option<FxHashMap<CompactArc<str>, Value>>,
transaction_id: Option<u64>,
}
impl ExpressionEval {
pub fn compile(expr: &Expression, columns: &[String]) -> Result<Self> {
let program = compile_expression(expr, columns)?;
Ok(Self {
program,
vm: ExprVM::new(),
params: CompactArc::new(ParamVec::new()),
named_params: Arc::new(FxHashMap::default()),
outer_row: None,
transaction_id: None,
})
}
pub fn compile_with_aliases(
expr: &Expression,
columns: &[String],
aliases: &[(String, usize)],
) -> Result<Self> {
let alias_map: StringMap<u16> = aliases
.iter()
.map(|(name, idx)| (name.to_lowercase(), *idx as u16))
.collect();
Self::compile_with_options(
expr,
columns,
None,
None,
Some(alias_map),
global_registry(),
)
}
pub fn compile_with_options(
expr: &Expression,
columns: &[String],
columns2: Option<&[String]>,
outer_columns: Option<&[String]>,
expression_aliases: Option<StringMap<u16>>,
function_registry: &FunctionRegistry,
) -> Result<Self> {
let mut ctx = CompileContext::new(columns, function_registry);
if let Some(cols2) = columns2 {
ctx = ctx.with_second_row(cols2);
}
if let Some(outer) = outer_columns {
ctx = ctx.with_outer_columns(outer);
}
if let Some(aliases) = expression_aliases {
ctx = ctx.with_expression_aliases(aliases);
}
let compiler = ExprCompiler::new(&ctx);
let program = compiler
.compile(expr)
.map(CompactArc::new)
.map_err(|e| Error::internal(format!("Compile error: {}", e)))?;
Ok(Self {
program,
vm: ExprVM::new(),
params: CompactArc::new(ParamVec::new()),
named_params: Arc::new(FxHashMap::default()),
outer_row: None,
transaction_id: None,
})
}
pub fn from_program(program: SharedProgram) -> Self {
Self {
program,
vm: ExprVM::new(),
params: CompactArc::new(ParamVec::new()),
named_params: Arc::new(FxHashMap::default()),
outer_row: None,
transaction_id: None,
}
}
pub fn with_params(mut self, params: ParamVec) -> Self {
self.params = CompactArc::new(params);
self
}
pub fn with_named_params(mut self, named_params: FxHashMap<String, Value>) -> Self {
self.named_params = Arc::new(named_params);
self
}
pub fn with_context(mut self, ctx: &ExecutionContext) -> Self {
self.params = CompactArc::clone(ctx.params_arc());
self.named_params = Arc::clone(ctx.named_params_arc());
if let Some(outer) = ctx.outer_row() {
let arc_map = outer.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
self.outer_row = Some(arc_map);
}
self.transaction_id = ctx.transaction_id();
self
}
pub fn with_transaction_id(mut self, txn_id: Option<u64>) -> Self {
self.transaction_id = txn_id;
self
}
pub fn set_outer_row(&mut self, outer: &FxHashMap<CompactArc<str>, Value>) {
let map = outer.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
self.outer_row = Some(map);
}
pub fn clear_outer_row(&mut self) {
self.outer_row = None;
}
#[inline]
pub fn eval(&mut self, row: &Row) -> Result<Value> {
let mut ctx = ExecuteContext::new(row);
if !self.params.is_empty() {
ctx = ctx.with_params(&self.params);
}
if !self.named_params.is_empty() {
ctx = ctx.with_named_params(&self.named_params);
}
if let Some(ref outer) = self.outer_row {
ctx = ctx.with_outer_row(outer);
}
ctx = ctx.with_transaction_id(self.transaction_id);
self.vm.execute_cow(&self.program, &ctx)
}
#[inline]
pub fn eval_bool(&mut self, row: &Row) -> bool {
let mut ctx = ExecuteContext::new(row);
if !self.params.is_empty() {
ctx = ctx.with_params(&self.params);
}
if !self.named_params.is_empty() {
ctx = ctx.with_named_params(&self.named_params);
}
if let Some(ref outer) = self.outer_row {
ctx = ctx.with_outer_row(outer);
}
ctx = ctx.with_transaction_id(self.transaction_id);
self.vm.execute_bool(&self.program, &ctx)
}
#[inline]
pub fn eval_bool_checked(&mut self, row: &Row) -> Result<bool> {
let mut ctx = ExecuteContext::new(row);
if !self.params.is_empty() {
ctx = ctx.with_params(&self.params);
}
if !self.named_params.is_empty() {
ctx = ctx.with_named_params(&self.named_params);
}
if let Some(ref outer) = self.outer_row {
ctx = ctx.with_outer_row(outer);
}
ctx = ctx.with_transaction_id(self.transaction_id);
self.vm.execute_bool_checked(&self.program, &ctx)
}
#[inline]
pub fn eval_join(&mut self, left: &Row, right: &Row) -> Result<Value> {
let ctx = ExecuteContext::for_join(left, right);
self.vm.execute_cow(&self.program, &ctx)
}
#[inline]
pub fn eval_join_bool(&mut self, left: &Row, right: &Row) -> bool {
let ctx = ExecuteContext::for_join(left, right);
self.vm.execute_bool(&self.program, &ctx)
}
#[inline]
pub fn eval_slice(&mut self, row: &Row) -> Result<Value> {
let mut ctx = ExecuteContext::new(row);
if !self.params.is_empty() {
ctx = ctx.with_params(&self.params);
}
if !self.named_params.is_empty() {
ctx = ctx.with_named_params(&self.named_params);
}
if let Some(ref outer) = self.outer_row {
ctx = ctx.with_outer_row(outer);
}
ctx = ctx.with_transaction_id(self.transaction_id);
self.vm.execute_cow(&self.program, &ctx)
}
#[inline]
pub fn eval_slice_bool(&mut self, row: &Row) -> bool {
let mut ctx = ExecuteContext::new(row);
if !self.params.is_empty() {
ctx = ctx.with_params(&self.params);
}
if !self.named_params.is_empty() {
ctx = ctx.with_named_params(&self.named_params);
}
if let Some(ref outer) = self.outer_row {
ctx = ctx.with_outer_row(outer);
}
ctx = ctx.with_transaction_id(self.transaction_id);
self.vm.execute_bool(&self.program, &ctx)
}
pub fn program(&self) -> &SharedProgram {
&self.program
}
}
pub struct MultiExpressionEval {
programs: Vec<SharedProgram>,
vm: ExprVM,
params: CompactArc<ParamVec>,
named_params: Arc<FxHashMap<String, Value>>,
transaction_id: Option<u64>,
}
impl MultiExpressionEval {
pub fn compile(exprs: &[Expression], columns: &[String]) -> Result<Self> {
let ctx = CompileContext::with_global_registry(columns);
let compiler = ExprCompiler::new(&ctx);
let programs = exprs
.iter()
.map(|expr| {
compiler
.compile(expr)
.map(CompactArc::new)
.map_err(|e| Error::internal(format!("Compile error: {}", e)))
})
.collect::<Result<Vec<_>>>()?;
Ok(Self {
programs,
vm: ExprVM::new(),
params: CompactArc::new(ParamVec::new()),
named_params: Arc::new(FxHashMap::default()),
transaction_id: None,
})
}
pub fn compile_with_aliases(
exprs: &[Expression],
columns: &[String],
aliases: &[(String, usize)],
) -> Result<Self> {
let alias_map: StringMap<u16> = aliases
.iter()
.map(|(name, idx)| (name.to_lowercase(), *idx as u16))
.collect();
let ctx = CompileContext::with_global_registry(columns).with_expression_aliases(alias_map);
let compiler = ExprCompiler::new(&ctx);
let programs = exprs
.iter()
.map(|expr| {
compiler
.compile(expr)
.map(CompactArc::new)
.map_err(|e| Error::internal(format!("Compile error: {}", e)))
})
.collect::<Result<Vec<_>>>()?;
Ok(Self {
programs,
vm: ExprVM::new(),
params: CompactArc::new(ParamVec::new()),
named_params: Arc::new(FxHashMap::default()),
transaction_id: None,
})
}
pub fn with_params(mut self, params: ParamVec) -> Self {
self.params = CompactArc::new(params);
self
}
pub fn with_context(mut self, ctx: &ExecutionContext) -> Self {
self.params = CompactArc::clone(ctx.params_arc());
self.named_params = Arc::clone(ctx.named_params_arc());
self.transaction_id = ctx.transaction_id();
self
}
#[inline]
pub fn eval_all(&mut self, row: &Row) -> Result<Vec<Value>> {
let mut ctx = ExecuteContext::new(row);
if !self.params.is_empty() {
ctx = ctx.with_params(&self.params);
}
if !self.named_params.is_empty() {
ctx = ctx.with_named_params(&self.named_params);
}
ctx = ctx.with_transaction_id(self.transaction_id);
self.programs
.iter()
.map(|prog| self.vm.execute_cow(prog, &ctx))
.collect()
}
#[inline]
pub fn eval_into(&mut self, row: &Row, output: &mut Vec<Value>) -> Result<()> {
let mut ctx = ExecuteContext::new(row);
if !self.params.is_empty() {
ctx = ctx.with_params(&self.params);
}
if !self.named_params.is_empty() {
ctx = ctx.with_named_params(&self.named_params);
}
ctx = ctx.with_transaction_id(self.transaction_id);
output.clear();
for prog in &self.programs {
output.push(self.vm.execute_cow(prog, &ctx)?);
}
Ok(())
}
pub fn len(&self) -> usize {
self.programs.len()
}
pub fn is_empty(&self) -> bool {
self.programs.is_empty()
}
}
pub type SharedProgram = CompactArc<Program>;
pub struct CompiledEvaluator<'a> {
function_registry: &'a FunctionRegistry,
columns: CompactArc<Vec<String>>,
columns_arc_id: usize,
columns2: Option<Vec<String>>,
outer_columns: Option<Vec<String>>,
params: CompactArc<ParamVec>,
named_params: Arc<FxHashMap<String, Value>>,
outer_row: Option<FxHashMap<CompactArc<str>, Value>>,
transaction_id: Option<u64>,
expression_aliases: StringMap<u16>,
column_aliases: StringMap<String>,
vm: ExprVM,
local_cache: FxHashMap<u64, SharedProgram>,
current_row: Option<Row>,
current_row2: Option<Row>,
}
impl<'a> CompiledEvaluator<'a> {
pub fn new(function_registry: &'a FunctionRegistry) -> Self {
Self {
function_registry,
columns: CompactArc::new(Vec::new()),
columns_arc_id: 0,
columns2: None,
outer_columns: None,
params: CompactArc::new(ParamVec::new()),
named_params: Arc::new(FxHashMap::default()),
outer_row: None,
transaction_id: None,
expression_aliases: StringMap::new(),
column_aliases: StringMap::new(),
vm: ExprVM::new(),
local_cache: FxHashMap::default(),
current_row: None,
current_row2: None,
}
}
pub fn with_defaults() -> CompiledEvaluator<'static> {
CompiledEvaluator::new(global_registry())
}
pub fn clear(&mut self) {
self.columns = CompactArc::new(Vec::new());
self.columns_arc_id = 0;
self.columns2 = None;
self.outer_columns = None;
self.params = CompactArc::new(ParamVec::new());
self.named_params = Arc::new(FxHashMap::default());
self.outer_row = None;
self.transaction_id = None;
self.expression_aliases.clear();
self.column_aliases.clear();
self.local_cache.clear();
self.current_row = None;
self.current_row2 = None;
}
pub fn set_transaction_id(&mut self, txn_id: u64) {
self.transaction_id = Some(txn_id);
}
pub fn with_params(mut self, params: ParamVec) -> Self {
self.params = CompactArc::new(params);
self
}
pub fn with_named_params(mut self, named_params: FxHashMap<String, Value>) -> Self {
self.named_params = Arc::new(named_params);
self
}
pub fn with_context(mut self, ctx: &ExecutionContext) -> Self {
self.params = CompactArc::clone(ctx.params_arc());
self.named_params = Arc::clone(ctx.named_params_arc());
if let Some(outer) = ctx.outer_row() {
let arc_map = outer.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
let outer_cols: Vec<String> = outer.keys().map(|k| k.to_string()).collect();
self.outer_row = Some(arc_map);
if !outer_cols.is_empty() {
self.outer_columns = Some(outer_cols);
self.local_cache.clear();
}
}
self.transaction_id = ctx.transaction_id();
self
}
pub fn with_row(mut self, row: Row, columns: &[String]) -> Self {
self.init_columns(columns);
let _ = row; self
}
pub fn init_columns(&mut self, columns: &[String]) {
let new_source_id = columns.as_ptr() as usize;
if self.columns_arc_id == new_source_id && self.columns.len() == columns.len() {
return;
}
self.columns = CompactArc::new(columns.to_vec());
self.columns_arc_id = new_source_id;
self.local_cache.clear();
}
#[inline]
pub fn init_columns_arc(&mut self, columns: CompactArc<Vec<String>>) {
let new_arc_id = CompactArc::as_ptr(&columns) as usize;
if self.columns_arc_id == new_arc_id {
return;
}
self.columns = columns;
self.columns_arc_id = new_arc_id;
self.local_cache.clear();
}
pub fn add_aggregate_aliases(&mut self, aliases: &[(String, usize)]) {
for (expr_name, idx) in aliases {
let lower = expr_name.to_lowercase();
self.expression_aliases.insert(lower, *idx as u16);
}
self.local_cache.clear();
}
pub fn add_expression_aliases(&mut self, aliases: &[(String, usize)]) {
for (expr_str, idx) in aliases {
let lower = expr_str.to_lowercase();
self.expression_aliases.insert(lower, *idx as u16);
}
self.local_cache.clear();
}
#[inline]
pub fn set_row_array(&mut self, row: &Row) {
self.current_row = Some(row.clone());
self.current_row2 = None;
}
#[inline]
pub fn set_join_rows(&mut self, left_row: &Row, right_row: &Row) {
self.current_row = Some(left_row.clone());
self.current_row2 = Some(right_row.clone());
}
pub fn init_join_columns(&mut self, left_columns: &[String], right_columns: &[String]) {
self.columns = CompactArc::new(left_columns.to_vec());
self.columns_arc_id = 0; self.columns2 = Some(right_columns.to_vec());
self.local_cache.clear();
}
#[inline]
pub fn set_outer_row(&mut self, outer_row: Option<&FxHashMap<CompactArc<str>, Value>>) {
if let Some(outer) = outer_row {
let map = outer.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
self.outer_row = Some(map);
} else {
self.outer_row = None;
}
}
#[inline]
pub fn set_outer_row_owned(&mut self, outer_row: FxHashMap<CompactArc<str>, Value>) {
let outer_cols: Vec<String> = outer_row.keys().map(|k| k.to_string()).collect();
self.outer_row = Some(outer_row);
if !outer_cols.is_empty() {
let mut sorted_cols = outer_cols;
sorted_cols.sort();
self.outer_columns = Some(sorted_cols);
self.local_cache.clear();
}
}
pub fn compile_shared(&mut self, expr: &Expression) -> Result<SharedProgram> {
self.get_or_compile(expr)
}
#[inline]
pub fn take_outer_row(&mut self) -> FxHashMap<CompactArc<str>, Value> {
self.outer_row.take().unwrap_or_default()
}
#[inline]
pub fn clear_outer_row(&mut self) {
self.outer_row = None;
}
pub fn init_outer_columns(&mut self, outer_columns: &[String]) {
self.outer_columns = Some(outer_columns.to_vec());
self.local_cache.clear();
}
pub fn has_outer_columns(&self) -> bool {
self.outer_columns.is_some()
}
pub fn get_outer_columns(&self) -> Option<&Vec<String>> {
self.outer_columns.as_ref()
}
pub fn clear_row(&mut self) {
self.current_row = None;
self.current_row2 = None;
}
#[inline]
fn expr_hash(&self, expr: &Expression) -> u64 {
let mut hasher = FxHasher::default();
Self::hash_expression(expr, &mut hasher);
hasher.finish()
}
fn hash_expression(expr: &Expression, hasher: &mut FxHasher) {
std::mem::discriminant(expr).hash(hasher);
match expr {
Expression::Identifier(id) => {
id.value_lower.hash(hasher);
}
Expression::QualifiedIdentifier(qid) => {
qid.qualifier.value_lower.hash(hasher);
qid.name.value_lower.hash(hasher);
}
Expression::IntegerLiteral(lit) => {
lit.value.hash(hasher);
}
Expression::FloatLiteral(lit) => {
lit.value.to_bits().hash(hasher);
}
Expression::StringLiteral(lit) => {
lit.value.hash(hasher);
lit.type_hint.hash(hasher);
}
Expression::BooleanLiteral(lit) => {
lit.value.hash(hasher);
}
Expression::NullLiteral(_) => {
}
Expression::IntervalLiteral(lit) => {
lit.value.hash(hasher);
lit.unit.hash(hasher);
}
Expression::Parameter(param) => {
param.index.hash(hasher);
param.name.hash(hasher);
}
Expression::Prefix(prefix) => {
std::mem::discriminant(&prefix.op_type).hash(hasher);
Self::hash_expression(&prefix.right, hasher);
}
Expression::Infix(infix) => {
std::mem::discriminant(&infix.op_type).hash(hasher);
Self::hash_expression(&infix.left, hasher);
Self::hash_expression(&infix.right, hasher);
}
Expression::List(list) => {
list.elements.len().hash(hasher);
for val in &list.elements {
Self::hash_expression(val, hasher);
}
}
Expression::Distinct(dist) => {
Self::hash_expression(&dist.expr, hasher);
}
Expression::Exists(exists) => {
(exists.subquery.as_ref() as *const _ as usize).hash(hasher);
}
Expression::AllAny(aa) => {
aa.operator.hash(hasher);
std::mem::discriminant(&aa.all_any_type).hash(hasher);
Self::hash_expression(&aa.left, hasher);
(aa.subquery.as_ref() as *const _ as usize).hash(hasher);
}
Expression::In(in_expr) => {
in_expr.not.hash(hasher);
Self::hash_expression(&in_expr.left, hasher);
Self::hash_expression(&in_expr.right, hasher);
}
Expression::InHashSet(in_hash) => {
in_hash.not.hash(hasher);
Self::hash_expression(&in_hash.column, hasher);
in_hash.values.len().hash(hasher);
}
Expression::Between(between) => {
between.not.hash(hasher);
Self::hash_expression(&between.expr, hasher);
Self::hash_expression(&between.lower, hasher);
Self::hash_expression(&between.upper, hasher);
}
Expression::Like(like) => {
like.operator.hash(hasher);
Self::hash_expression(&like.left, hasher);
Self::hash_expression(&like.pattern, hasher);
if let Some(ref escape) = like.escape {
true.hash(hasher);
Self::hash_expression(escape, hasher);
} else {
false.hash(hasher);
}
}
Expression::ScalarSubquery(sq) => {
(sq.subquery.as_ref() as *const _ as usize).hash(hasher);
}
Expression::ExpressionList(list) => {
list.expressions.len().hash(hasher);
for expr in &list.expressions {
Self::hash_expression(expr, hasher);
}
}
Expression::Case(case) => {
if let Some(ref val) = case.value {
true.hash(hasher);
Self::hash_expression(val, hasher);
} else {
false.hash(hasher);
}
case.when_clauses.len().hash(hasher);
for when_clause in &case.when_clauses {
Self::hash_expression(&when_clause.condition, hasher);
Self::hash_expression(&when_clause.then_result, hasher);
}
if let Some(ref else_val) = case.else_value {
true.hash(hasher);
Self::hash_expression(else_val, hasher);
} else {
false.hash(hasher);
}
}
Expression::Cast(cast) => {
Self::hash_expression(&cast.expr, hasher);
cast.type_name.hash(hasher);
}
Expression::FunctionCall(func) => {
func.function.hash(hasher);
func.is_distinct.hash(hasher);
func.arguments.len().hash(hasher);
for arg in &func.arguments {
Self::hash_expression(arg, hasher);
}
if let Some(ref filter) = func.filter {
true.hash(hasher);
Self::hash_expression(filter, hasher);
} else {
false.hash(hasher);
}
}
Expression::Aliased(aliased) => {
aliased.alias.value_lower.hash(hasher);
Self::hash_expression(&aliased.expression, hasher);
}
Expression::Window(window) => {
window.function.function.hash(hasher);
window.function.is_distinct.hash(hasher);
window.function.arguments.len().hash(hasher);
for arg in &window.function.arguments {
Self::hash_expression(arg, hasher);
}
window.partition_by.len().hash(hasher);
for expr in &window.partition_by {
Self::hash_expression(expr, hasher);
}
window.order_by.len().hash(hasher);
for order in &window.order_by {
Self::hash_expression(&order.expression, hasher);
order.ascending.hash(hasher);
order.nulls_first.hash(hasher);
}
}
Expression::TableSource(ts) => {
ts.name.value_lower.hash(hasher);
if let Some(ref alias) = ts.alias {
true.hash(hasher);
alias.value_lower.hash(hasher);
} else {
false.hash(hasher);
}
}
Expression::JoinSource(js) => {
(js.as_ref() as *const _ as usize).hash(hasher);
}
Expression::SubquerySource(sq) => {
if let Some(ref alias) = sq.alias {
true.hash(hasher);
alias.value_lower.hash(hasher);
} else {
false.hash(hasher);
}
(sq.subquery.as_ref() as *const _ as usize).hash(hasher);
}
Expression::ValuesSource(vs) => {
if let Some(ref alias) = vs.alias {
true.hash(hasher);
alias.value_lower.hash(hasher);
} else {
false.hash(hasher);
}
vs.rows.len().hash(hasher);
}
Expression::CteReference(cte) => {
cte.name.value_lower.hash(hasher);
}
Expression::FunctionTableSource(fts) => {
fts.function.value_lower.hash(hasher);
for arg in &fts.arguments {
Self::hash_expression(arg, hasher);
}
}
Expression::Star(_) => {
}
Expression::QualifiedStar(qs) => {
qs.qualifier.hash(hasher);
}
Expression::Default(_) => {
}
}
}
fn get_or_compile(&mut self, expr: &Expression) -> Result<SharedProgram> {
let expr_key = self.expr_hash(expr);
if let Some(program) = self.local_cache.get(&expr_key) {
return Ok(CompactArc::clone(program));
}
let program = CompactArc::new(self.compile_expression(expr)?);
self.local_cache
.insert(expr_key, CompactArc::clone(&program));
Ok(program)
}
fn compile_expression(&self, expr: &Expression) -> Result<Program> {
let mut ctx = CompileContext::new(&self.columns, self.function_registry);
if let Some(ref cols2) = self.columns2 {
ctx = ctx.with_second_row(cols2);
}
if let Some(ref outer_cols) = self.outer_columns {
ctx = ctx.with_outer_columns(outer_cols);
}
if !self.expression_aliases.is_empty() {
ctx = ctx.with_expression_aliases(self.expression_aliases.clone());
}
if !self.column_aliases.is_empty() {
ctx = ctx.with_column_aliases(self.column_aliases.clone());
}
let compiler = ExprCompiler::new(&ctx);
compiler
.compile(expr)
.map_err(|e| Error::internal(format!("Compile error: {}", e)))
}
pub fn evaluate(&mut self, expr: &Expression) -> Result<Value> {
let program = self.get_or_compile(expr)?;
static EMPTY_ROW: std::sync::LazyLock<Row> = std::sync::LazyLock::new(Row::new);
let row = self.current_row.as_ref().unwrap_or(&EMPTY_ROW);
let row2 = self.current_row2.as_ref();
let mut ctx = if let Some(r2) = row2 {
ExecuteContext::for_join(row, r2)
} else {
ExecuteContext::new(row)
};
if !self.params.is_empty() {
ctx = ctx.with_params(&self.params);
}
if !self.named_params.is_empty() {
ctx = ctx.with_named_params(&self.named_params);
}
if let Some(ref outer) = self.outer_row {
ctx = ctx.with_outer_row(outer);
}
ctx = ctx.with_transaction_id(self.transaction_id);
self.vm.execute_cow(&program, &ctx)
}
pub fn evaluate_bool(&mut self, expr: &Expression) -> Result<bool> {
let program = self.get_or_compile(expr)?;
static EMPTY_ROW: std::sync::LazyLock<Row> = std::sync::LazyLock::new(Row::new);
let row = self.current_row.as_ref().unwrap_or(&EMPTY_ROW);
let row2 = self.current_row2.as_ref();
let mut ctx = if let Some(r2) = row2 {
ExecuteContext::for_join(row, r2)
} else {
ExecuteContext::new(row)
};
if !self.params.is_empty() {
ctx = ctx.with_params(&self.params);
}
if !self.named_params.is_empty() {
ctx = ctx.with_named_params(&self.named_params);
}
if let Some(ref outer) = self.outer_row {
ctx = ctx.with_outer_row(outer);
}
ctx = ctx.with_transaction_id(self.transaction_id);
Ok(self.vm.execute_bool(&program, &ctx))
}
}
impl Default for CompiledEvaluator<'static> {
fn default() -> Self {
Self::with_defaults()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::ast::{
Expression, Identifier, InfixExpression, InfixOperator, IntegerLiteral,
};
use crate::parser::token::{Position, Token, TokenType};
fn dummy_token() -> Token {
Token::new(TokenType::Eof, "", Position::default())
}
fn make_identifier(name: &str) -> Expression {
Expression::Identifier(Identifier {
token: dummy_token(),
value: name.into(),
value_lower: name.to_lowercase().into(),
})
}
fn make_int_literal(value: i64) -> Expression {
Expression::IntegerLiteral(IntegerLiteral {
token: dummy_token(),
value,
})
}
fn make_infix(left: Expression, op: InfixOperator, right: Expression) -> Expression {
let op_str = match op {
InfixOperator::GreaterThan => ">",
InfixOperator::LessThan => "<",
InfixOperator::Equal => "=",
InfixOperator::Add => "+",
InfixOperator::Multiply => "*",
_ => "?",
};
Expression::Infix(InfixExpression {
token: dummy_token(),
left: Box::new(left),
operator: op_str.into(),
op_type: op,
right: Box::new(right),
})
}
#[test]
fn test_compute_expression_hash_same_expr() {
let expr1 = make_int_literal(42);
let expr2 = make_int_literal(42);
assert_eq!(
compute_expression_hash(&expr1),
compute_expression_hash(&expr2)
);
}
#[test]
fn test_compute_expression_hash_different_expr() {
let expr1 = make_int_literal(42);
let expr2 = make_int_literal(43);
assert_ne!(
compute_expression_hash(&expr1),
compute_expression_hash(&expr2)
);
}
#[test]
fn test_compute_expression_hash_complex() {
let expr1 = make_infix(
make_identifier("col"),
InfixOperator::GreaterThan,
make_int_literal(5),
);
let expr2 = make_infix(
make_identifier("col"),
InfixOperator::GreaterThan,
make_int_literal(5),
);
assert_eq!(
compute_expression_hash(&expr1),
compute_expression_hash(&expr2)
);
}
#[test]
fn test_compile_expression_basic() {
let expr = make_infix(
make_identifier("col"),
InfixOperator::GreaterThan,
make_int_literal(5),
);
let columns = vec!["col".to_string()];
let program = compile_expression(&expr, &columns);
assert!(program.is_ok());
}
#[test]
fn test_compile_expression_unknown_column() {
let expr = make_infix(
make_identifier("unknown_col"),
InfixOperator::GreaterThan,
make_int_literal(5),
);
let columns = vec!["col".to_string()];
let program = compile_expression(&expr, &columns);
assert!(program.is_err());
}
#[test]
fn test_row_filter_new() {
let expr = make_infix(
make_identifier("col"),
InfixOperator::GreaterThan,
make_int_literal(5),
);
let columns = vec!["col".to_string()];
let filter = RowFilter::new(&expr, &columns);
assert!(filter.is_ok());
}
#[test]
fn test_row_filter_matches_true() {
let expr = make_infix(
make_identifier("col"),
InfixOperator::GreaterThan,
make_int_literal(5),
);
let columns = vec!["col".to_string()];
let filter = RowFilter::new(&expr, &columns).unwrap();
let row = Row::from(vec![Value::Integer(10)]);
assert!(filter.matches(&row));
}
#[test]
fn test_row_filter_matches_false() {
let expr = make_infix(
make_identifier("col"),
InfixOperator::GreaterThan,
make_int_literal(5),
);
let columns = vec!["col".to_string()];
let filter = RowFilter::new(&expr, &columns).unwrap();
let row = Row::from(vec![Value::Integer(3)]);
assert!(!filter.matches(&row));
}
#[test]
fn test_row_filter_evaluate() {
let expr = make_infix(
make_identifier("col"),
InfixOperator::Add,
make_int_literal(10),
);
let columns = vec!["col".to_string()];
let filter = RowFilter::new(&expr, &columns).unwrap();
let row = Row::from(vec![Value::Integer(5)]);
let result = filter.evaluate(&row).unwrap();
assert_eq!(result, Value::Integer(15));
}
#[test]
fn test_row_filter_clone() {
let expr = make_infix(
make_identifier("col"),
InfixOperator::GreaterThan,
make_int_literal(5),
);
let columns = vec!["col".to_string()];
let filter = RowFilter::new(&expr, &columns).unwrap();
let cloned = filter.clone();
let row = Row::from(vec![Value::Integer(10)]);
assert!(filter.matches(&row));
assert!(cloned.matches(&row));
}
#[test]
fn test_expression_eval_compile() {
let expr = make_infix(
make_identifier("col"),
InfixOperator::GreaterThan,
make_int_literal(5),
);
let columns = vec!["col".to_string()];
let eval = ExpressionEval::compile(&expr, &columns);
assert!(eval.is_ok());
}
#[test]
fn test_expression_eval_eval() {
let expr = make_infix(
make_identifier("col"),
InfixOperator::Add,
make_int_literal(10),
);
let columns = vec!["col".to_string()];
let mut eval = ExpressionEval::compile(&expr, &columns).unwrap();
let row = Row::from(vec![Value::Integer(5)]);
let result = eval.eval(&row).unwrap();
assert_eq!(result, Value::Integer(15));
}
#[test]
fn test_expression_eval_eval_bool() {
let expr = make_infix(
make_identifier("col"),
InfixOperator::GreaterThan,
make_int_literal(5),
);
let columns = vec!["col".to_string()];
let mut eval = ExpressionEval::compile(&expr, &columns).unwrap();
let row = Row::from(vec![Value::Integer(10)]);
assert!(eval.eval_bool(&row));
let row = Row::from(vec![Value::Integer(3)]);
assert!(!eval.eval_bool(&row));
}
#[test]
fn test_multi_expression_eval_compile() {
let expr1 = make_infix(
make_identifier("col"),
InfixOperator::Add,
make_int_literal(10),
);
let expr2 = make_infix(
make_identifier("col"),
InfixOperator::Multiply,
make_int_literal(2),
);
let columns = vec!["col".to_string()];
let eval = MultiExpressionEval::compile(&[expr1, expr2], &columns);
assert!(eval.is_ok());
assert_eq!(eval.unwrap().len(), 2);
}
#[test]
fn test_multi_expression_eval_all() {
let expr1 = make_infix(
make_identifier("col"),
InfixOperator::Add,
make_int_literal(10),
);
let expr2 = make_infix(
make_identifier("col"),
InfixOperator::Multiply,
make_int_literal(2),
);
let columns = vec!["col".to_string()];
let mut eval = MultiExpressionEval::compile(&[expr1, expr2], &columns).unwrap();
let row = Row::from(vec![Value::Integer(5)]);
let results = eval.eval_all(&row).unwrap();
assert_eq!(results.len(), 2);
assert_eq!(results[0], Value::Integer(15)); assert_eq!(results[1], Value::Integer(10)); }
#[test]
fn test_compiled_evaluator_with_defaults() {
let eval = CompiledEvaluator::with_defaults();
assert!(eval.columns.is_empty());
}
#[test]
fn test_compiled_evaluator_init_columns() {
let mut eval = CompiledEvaluator::with_defaults();
eval.init_columns(&["col1".to_string(), "col2".to_string()]);
assert_eq!(eval.columns.len(), 2);
}
#[test]
fn test_compiled_evaluator_evaluate_bool() {
let mut eval = CompiledEvaluator::with_defaults();
eval.init_columns(&["col".to_string()]);
let row = Row::from(vec![Value::Integer(10)]);
eval.set_row_array(&row);
let expr = make_infix(
make_identifier("col"),
InfixOperator::GreaterThan,
make_int_literal(5),
);
let result = eval.evaluate_bool(&expr);
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_compiled_evaluator_evaluate() {
let mut eval = CompiledEvaluator::with_defaults();
eval.init_columns(&["col".to_string()]);
let row = Row::from(vec![Value::Integer(5)]);
eval.set_row_array(&row);
let expr = make_infix(
make_identifier("col"),
InfixOperator::Add,
make_int_literal(10),
);
let result = eval.evaluate(&expr);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(15));
}
#[test]
fn test_compiled_evaluator_default() {
let eval = CompiledEvaluator::default();
assert!(eval.columns.is_empty());
}
}