mod pattern;
mod specialized;
#[cfg(feature = "parallel")]
use std::sync::Arc;
use pattern::PredicatePattern;
use specialized::create_evaluator;
#[cfg(feature = "parallel")]
use super::parallel::ParallelConfig;
use crate::{
errors::ExecutorError,
evaluator::{CombinedExpressionEvaluator, ExpressionEvaluator},
};
#[inline(always)]
fn string_to_truthy(s: &str) -> bool {
if s.is_empty() {
return false;
}
let trimmed = s.trim();
if trimmed.is_empty() {
return false;
}
let mut end = 0;
let mut has_dot = false;
let mut has_digit = false;
let chars: Vec<char> = trimmed.chars().collect();
if !chars.is_empty() && (chars[0] == '-' || chars[0] == '+') {
end = 1;
}
while end < chars.len() {
let c = chars[end];
if c.is_ascii_digit() {
has_digit = true;
end += 1;
} else if c == '.' && !has_dot {
has_dot = true;
end += 1;
} else {
break;
}
}
if !has_digit {
return false;
}
let num_str: String = chars[..end].iter().collect();
match num_str.parse::<f64>() {
Ok(n) => n != 0.0,
Err(_) => false,
}
}
#[cfg(feature = "parallel")]
#[inline(always)]
fn is_truthy_combined(value: &vibesql_types::SqlValue) -> Result<bool, ExecutorError> {
use vibesql_types::SqlValue;
match value {
SqlValue::Boolean(b) => Ok(*b),
SqlValue::Null => Ok(false),
SqlValue::Integer(n) => Ok(*n != 0),
SqlValue::Smallint(n) => Ok(*n != 0),
SqlValue::Bigint(n) => Ok(*n != 0),
SqlValue::Float(f) => Ok(*f != 0.0),
SqlValue::Real(f) => Ok(*f != 0.0),
SqlValue::Double(f) => Ok(*f != 0.0),
SqlValue::Numeric(f) => Ok(*f != 0.0),
SqlValue::Varchar(s) | SqlValue::Character(s) => Ok(string_to_truthy(&s)),
other => Err(ExecutorError::InvalidWhereClause(format!(
"WHERE clause must evaluate to boolean, got: {:?}",
other
))),
}
}
#[inline(always)]
fn is_truthy_basic(value: &vibesql_types::SqlValue) -> Result<bool, ExecutorError> {
use vibesql_types::SqlValue;
match value {
SqlValue::Boolean(b) => Ok(*b),
SqlValue::Null => Ok(false),
SqlValue::Integer(n) => Ok(*n != 0),
SqlValue::Smallint(n) => Ok(*n != 0),
SqlValue::Bigint(n) => Ok(*n != 0),
SqlValue::Float(f) => Ok(*f != 0.0),
SqlValue::Real(f) => Ok(*f != 0.0),
SqlValue::Double(f) => Ok(*f != 0.0),
SqlValue::Numeric(f) => Ok(*f != 0.0),
SqlValue::Varchar(s) | SqlValue::Character(s) => Ok(string_to_truthy(&s)),
other => Err(ExecutorError::InvalidWhereClause(format!(
"WHERE must evaluate to boolean, got: {:?}",
other
))),
}
}
pub(super) fn apply_where_filter_combined<'a>(
rows: Vec<vibesql_storage::Row>,
where_expr: Option<&vibesql_ast::Expression>,
evaluator: &CombinedExpressionEvaluator,
executor: &crate::SelectExecutor<'a>,
) -> Result<Vec<vibesql_storage::Row>, ExecutorError> {
if where_expr.is_none() {
return Ok(rows);
}
let where_expr = where_expr.unwrap();
let pattern = PredicatePattern::from_where_clause(where_expr, evaluator.schema());
let specialized_eval = create_evaluator(&pattern);
let mut filtered_rows = executor.query_buffer_pool().get_row_buffer(rows.len());
let mut rows_processed = 0;
const CHECK_INTERVAL: usize = 1000;
if let Some(fast_evaluator) = specialized_eval {
for row in rows {
rows_processed += 1;
if rows_processed % CHECK_INTERVAL == 0 {
executor.check_timeout()?;
}
if fast_evaluator.evaluate(&row)? {
filtered_rows.push(row);
}
}
} else {
for row in rows {
rows_processed += 1;
if rows_processed % CHECK_INTERVAL == 0 {
executor.check_timeout()?;
}
let include_row = match evaluator.eval(where_expr, &row)? {
vibesql_types::SqlValue::Boolean(true) => true,
vibesql_types::SqlValue::Boolean(false) | vibesql_types::SqlValue::Null => false,
vibesql_types::SqlValue::Integer(0) => false,
vibesql_types::SqlValue::Integer(_) => true,
vibesql_types::SqlValue::Smallint(0) => false,
vibesql_types::SqlValue::Smallint(_) => true,
vibesql_types::SqlValue::Bigint(0) => false,
vibesql_types::SqlValue::Bigint(_) => true,
vibesql_types::SqlValue::Float(0.0) => false,
vibesql_types::SqlValue::Float(_) => true,
vibesql_types::SqlValue::Real(0.0) => false,
vibesql_types::SqlValue::Real(_) => true,
vibesql_types::SqlValue::Double(0.0) => false,
vibesql_types::SqlValue::Double(_) => true,
vibesql_types::SqlValue::Numeric(n) if n == 0.0 => false,
vibesql_types::SqlValue::Numeric(_) => true,
vibesql_types::SqlValue::Varchar(ref s)
| vibesql_types::SqlValue::Character(ref s) => string_to_truthy(s),
other => {
return Err(ExecutorError::InvalidWhereClause(format!(
"WHERE clause must evaluate to boolean, got: {:?}",
other
)))
}
};
if include_row {
filtered_rows.push(row);
}
}
evaluator.clear_cse_cache();
}
let result = std::mem::take(&mut filtered_rows);
executor.query_buffer_pool().return_row_buffer(filtered_rows);
Ok(result)
}
#[allow(dead_code)]
pub(super) fn apply_where_filter_basic<'a>(
rows: Vec<vibesql_storage::Row>,
where_expr: Option<&vibesql_ast::Expression>,
evaluator: &ExpressionEvaluator,
executor: &crate::SelectExecutor<'a>,
) -> Result<Vec<vibesql_storage::Row>, ExecutorError> {
if where_expr.is_none() {
return Ok(rows);
}
let where_expr = where_expr.unwrap();
let mut filtered_rows = executor.query_buffer_pool().get_row_buffer(rows.len());
let mut rows_processed = 0;
const CHECK_INTERVAL: usize = 1000;
for row in rows.into_iter() {
rows_processed += 1;
if rows_processed % CHECK_INTERVAL == 0 {
executor.check_timeout()?;
}
let value = evaluator.eval(where_expr, &row)?;
let include_row = is_truthy_basic(&value)?;
if include_row {
filtered_rows.push(row); }
}
evaluator.clear_cse_cache();
let result = std::mem::take(&mut filtered_rows);
executor.query_buffer_pool().return_row_buffer(filtered_rows);
Ok(result)
}
#[cfg(feature = "parallel")]
#[allow(dead_code)]
pub(super) fn apply_where_filter_combined_parallel<'a>(
rows: Vec<vibesql_storage::Row>,
where_expr: Option<&vibesql_ast::Expression>,
evaluator: &CombinedExpressionEvaluator,
_executor: &crate::SelectExecutor<'a>,
) -> Result<Vec<vibesql_storage::Row>, ExecutorError> {
use super::{
morsel::{morsel_parallel_filter, MorselConfig},
vectorized::compiled_predicate::{CompiledOrClause, CompiledWhereClause},
};
if where_expr.is_none() {
return Ok(rows);
}
let config = ParallelConfig::global();
if !config.should_parallelize_scan(rows.len()) {
return apply_where_filter_combined(rows, where_expr, evaluator, _executor);
}
let where_expr = where_expr.unwrap();
let compiled_and = CompiledWhereClause::try_compile(where_expr, evaluator.schema());
let compiled_or = if compiled_and.is_none() {
CompiledOrClause::try_compile(where_expr, evaluator.schema())
} else {
None
};
if std::env::var("OR_COMPILE_DEBUG").is_ok() {
eprintln!(
"[PARALLEL_COMPILE] AND compiled: {}, OR compiled: {}, using MORSEL execution",
compiled_and.is_some(),
compiled_or.is_some()
);
}
let morsel_config = MorselConfig::optimal();
if let Some(compiled) = compiled_and {
let compiled_arc = Arc::new(compiled);
let filtered = morsel_parallel_filter(&rows, &morsel_config, |row| {
compiled_arc.evaluate(row).unwrap_or(false)
});
return Ok(filtered);
}
if let Some(compiled) = compiled_or {
let compiled_arc = Arc::new(compiled);
let filtered = morsel_parallel_filter(&rows, &morsel_config, |row| {
compiled_arc.evaluate(row).unwrap_or(false)
});
return Ok(filtered);
}
let where_expr_arc = Arc::new(where_expr.clone());
let (schema, database, outer_row, outer_schema, window_mapping, cte_context, enable_cse) =
evaluator.get_parallel_components();
let filtered = morsel_parallel_filter(&rows, &morsel_config, |row| {
let thread_evaluator = CombinedExpressionEvaluator::from_parallel_components(
schema,
database,
outer_row,
outer_schema,
window_mapping,
cte_context,
enable_cse,
);
match thread_evaluator.eval(&where_expr_arc, row) {
Ok(value) => is_truthy_combined(&value).unwrap_or(false),
Err(_) => false, }
});
Ok(filtered)
}
pub(crate) fn apply_where_filter_combined_auto<'a>(
rows: Vec<vibesql_storage::Row>,
where_expr: Option<&vibesql_ast::Expression>,
evaluator: &CombinedExpressionEvaluator,
executor: &crate::SelectExecutor<'a>,
) -> Result<Vec<vibesql_storage::Row>, ExecutorError> {
if std::env::var("OR_COMPILE_DEBUG").is_ok() {
eprintln!(
"[FILTER_AUTO_ENTRY] Called with {} rows, where_expr: {}",
rows.len(),
where_expr.is_some()
);
}
if where_expr.is_none() {
return Ok(rows);
}
#[cfg(feature = "parallel")]
{
let row_count = rows.len();
let config = ParallelConfig::global();
if config.should_parallelize_scan(row_count) {
if std::env::var("OR_COMPILE_DEBUG").is_ok() {
eprintln!("[FILTER_AUTO] Using PARALLEL path for {} rows", row_count);
}
return apply_where_filter_combined_parallel(rows, where_expr, evaluator, executor);
}
}
if std::env::var("OR_COMPILE_DEBUG").is_ok() {
eprintln!("[FILTER_AUTO] Calling vectorized filter on {} rows", rows.len());
}
super::vectorized::apply_where_filter_vectorized(rows, where_expr, evaluator, executor)
}