use vibesql_ast::Expression;
use vibesql_storage::Row;
use super::{
compiled_predicate::{CompiledOrClause, CompiledWhereClause},
VECTORIZE_THRESHOLD,
};
use crate::{errors::ExecutorError, evaluator::CombinedExpressionEvaluator};
pub fn apply_where_filter_vectorized<'a>(
rows: Vec<Row>,
where_expr: Option<&Expression>,
evaluator: &CombinedExpressionEvaluator,
executor: &crate::SelectExecutor<'a>,
) -> Result<Vec<Row>, ExecutorError> {
if where_expr.is_none() {
return Ok(rows);
}
let where_expr = where_expr.unwrap();
let row_count = rows.len();
if row_count < VECTORIZE_THRESHOLD {
return super::super::filter::apply_where_filter_combined(
rows,
Some(where_expr),
evaluator,
executor,
);
}
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
};
let mut filtered_rows = executor.query_buffer_pool().get_row_buffer(row_count);
let mut rows_processed = 0;
const CHECK_INTERVAL: usize = 1000;
if std::env::var("OR_COMPILE_DEBUG").is_ok() {
eprintln!(
"[OR_COMPILE] AND compiled: {}, OR compiled: {}",
compiled_and.is_some(),
compiled_or.is_some()
);
}
if let Some(compiled_pred) = compiled_and {
for row in rows.into_iter() {
rows_processed += 1;
if rows_processed % CHECK_INTERVAL == 0 {
executor.check_timeout()?;
}
if compiled_pred.evaluate(&row)? {
filtered_rows.push(row);
}
}
} else if let Some(compiled_or_pred) = compiled_or {
for row in rows.into_iter() {
rows_processed += 1;
if rows_processed % CHECK_INTERVAL == 0 {
executor.check_timeout()?;
}
if compiled_or_pred.evaluate(&row)? {
filtered_rows.push(row);
}
}
} else {
for row in rows.into_iter() {
rows_processed += 1;
if rows_processed % CHECK_INTERVAL == 0 {
executor.check_timeout()?;
}
let include_row = evaluate_predicate(&row, where_expr, evaluator)?;
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)
}
#[inline(always)]
fn evaluate_predicate(
row: &Row,
where_expr: &Expression,
evaluator: &CombinedExpressionEvaluator,
) -> Result<bool, ExecutorError> {
let value = evaluator.eval(where_expr, row)?;
is_truthy(&value)
}
#[inline(always)]
fn is_truthy(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 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();
num_str.parse::<f64>().map(|n| n != 0.0).unwrap_or(false)
}
#[cfg(test)]
#[allow(clippy::assertions_on_constants)]
mod tests {
use super::*;
#[test]
fn test_vectorize_threshold() {
assert!(VECTORIZE_THRESHOLD > 0);
assert!(VECTORIZE_THRESHOLD >= 100);
}
}