support column comparisons in the filter operator

We currently only support column / literal comparisons in the filter
operator. But with JOINs, comparisons are usually against two columns.

Do the work to support it.
This commit is contained in:
Glauber Costa
2025-09-18 11:23:47 -05:00
parent 47097fbec6
commit 627f61aa81
2 changed files with 167 additions and 3 deletions

View File

@@ -1376,10 +1376,84 @@ impl DbspCompiler {
match expr {
LogicalExpr::BinaryExpr { left, op, right } => {
// Extract column name and value for simple predicates
if let (LogicalExpr::Column(col), LogicalExpr::Literal(val)) =
// First check for column-to-column comparisons
if let (LogicalExpr::Column(left_col), LogicalExpr::Column(right_col)) =
(left.as_ref(), right.as_ref())
{
// Resolve column name to index using the schema
// Resolve both column names to indices
let left_idx = schema
.columns
.iter()
.position(|c| c.name == left_col.name)
.ok_or_else(|| {
crate::LimboError::ParseError(format!(
"Column '{}' not found in schema for filter",
left_col.name
))
})?;
let right_idx = schema
.columns
.iter()
.position(|c| c.name == right_col.name)
.ok_or_else(|| {
crate::LimboError::ParseError(format!(
"Column '{}' not found in schema for filter",
right_col.name
))
})?;
match op {
BinaryOperator::Equals => Ok(FilterPredicate::ColumnEquals {
left_idx,
right_idx,
}),
BinaryOperator::NotEquals => Ok(FilterPredicate::ColumnNotEquals {
left_idx,
right_idx,
}),
BinaryOperator::Greater => Ok(FilterPredicate::ColumnGreaterThan {
left_idx,
right_idx,
}),
BinaryOperator::GreaterEquals => {
Ok(FilterPredicate::ColumnGreaterThanOrEqual {
left_idx,
right_idx,
})
}
BinaryOperator::Less => Ok(FilterPredicate::ColumnLessThan {
left_idx,
right_idx,
}),
BinaryOperator::LessEquals => Ok(FilterPredicate::ColumnLessThanOrEqual {
left_idx,
right_idx,
}),
BinaryOperator::And | BinaryOperator::Or => {
// Handle logical operators recursively
let left_pred = Self::compile_filter_predicate(left, schema)?;
let right_pred = Self::compile_filter_predicate(right, schema)?;
match op {
BinaryOperator::And => Ok(FilterPredicate::And(
Box::new(left_pred),
Box::new(right_pred),
)),
BinaryOperator::Or => Ok(FilterPredicate::Or(
Box::new(left_pred),
Box::new(right_pred),
)),
_ => unreachable!(),
}
}
_ => Err(LimboError::ParseError(format!(
"Unsupported operator in filter: {op:?}"
))),
}
} else if let (LogicalExpr::Column(col), LogicalExpr::Literal(val)) =
(left.as_ref(), right.as_ref())
{
// Column-to-literal comparisons
let column_idx = schema
.columns
.iter()
@@ -1455,7 +1529,7 @@ impl DbspCompiler {
}
} else {
Err(LimboError::ParseError(
"Filter predicate must be column op value".to_string(),
"Filter predicate must be column op value or column op column".to_string(),
))
}
}

View File

@@ -25,6 +25,20 @@ pub enum FilterPredicate {
LessThan { column_idx: usize, value: Value },
/// Column <= value (using column index)
LessThanOrEqual { column_idx: usize, value: Value },
/// Column = Column comparisons
ColumnEquals { left_idx: usize, right_idx: usize },
/// Column != Column comparisons
ColumnNotEquals { left_idx: usize, right_idx: usize },
/// Column > Column comparisons
ColumnGreaterThan { left_idx: usize, right_idx: usize },
/// Column >= Column comparisons
ColumnGreaterThanOrEqual { left_idx: usize, right_idx: usize },
/// Column < Column comparisons
ColumnLessThan { left_idx: usize, right_idx: usize },
/// Column <= Column comparisons
ColumnLessThanOrEqual { left_idx: usize, right_idx: usize },
/// Logical AND of two predicates
And(Box<FilterPredicate>, Box<FilterPredicate>),
/// Logical OR of two predicates
@@ -124,6 +138,82 @@ impl FilterOperator {
let right_filter = FilterOperator::new((**right).clone());
left_filter.evaluate_predicate(values) || right_filter.evaluate_predicate(values)
}
// Column-to-column comparisons
FilterPredicate::ColumnEquals {
left_idx,
right_idx,
} => {
if let (Some(left), Some(right)) = (values.get(*left_idx), values.get(*right_idx)) {
return left == right;
}
false
}
FilterPredicate::ColumnNotEquals {
left_idx,
right_idx,
} => {
if let (Some(left), Some(right)) = (values.get(*left_idx), values.get(*right_idx)) {
return left != right;
}
false
}
FilterPredicate::ColumnGreaterThan {
left_idx,
right_idx,
} => {
if let (Some(left), Some(right)) = (values.get(*left_idx), values.get(*right_idx)) {
match (left, right) {
(Value::Integer(a), Value::Integer(b)) => return a > b,
(Value::Float(a), Value::Float(b)) => return a > b,
(Value::Text(a), Value::Text(b)) => return a.as_str() > b.as_str(),
_ => {}
}
}
false
}
FilterPredicate::ColumnGreaterThanOrEqual {
left_idx,
right_idx,
} => {
if let (Some(left), Some(right)) = (values.get(*left_idx), values.get(*right_idx)) {
match (left, right) {
(Value::Integer(a), Value::Integer(b)) => return a >= b,
(Value::Float(a), Value::Float(b)) => return a >= b,
(Value::Text(a), Value::Text(b)) => return a.as_str() >= b.as_str(),
_ => {}
}
}
false
}
FilterPredicate::ColumnLessThan {
left_idx,
right_idx,
} => {
if let (Some(left), Some(right)) = (values.get(*left_idx), values.get(*right_idx)) {
match (left, right) {
(Value::Integer(a), Value::Integer(b)) => return a < b,
(Value::Float(a), Value::Float(b)) => return a < b,
(Value::Text(a), Value::Text(b)) => return a.as_str() < b.as_str(),
_ => {}
}
}
false
}
FilterPredicate::ColumnLessThanOrEqual {
left_idx,
right_idx,
} => {
if let (Some(left), Some(right)) = (values.get(*left_idx), values.get(*right_idx)) {
match (left, right) {
(Value::Integer(a), Value::Integer(b)) => return a <= b,
(Value::Float(a), Value::Float(b)) => return a <= b,
(Value::Text(a), Value::Text(b)) => return a.as_str() <= b.as_str(),
_ => {}
}
}
false
}
}
}
}