use vibesql_ast::{BinaryOperator, Expression};
pub(super) fn expression_depth(expr: &Expression) -> usize {
match expr {
Expression::ColumnRef(_) | Expression::Literal(_) => 1,
Expression::BinaryOp { left, right, .. } => {
1 + expression_depth(left).max(expression_depth(right))
}
Expression::UnaryOp { expr: inner, .. } => 1 + expression_depth(inner),
_ => 1,
}
}
pub(super) fn hash_expression(expr: &Expression) -> u64 {
use std::{collections::hash_map::DefaultHasher, hash::Hasher};
let mut hasher = DefaultHasher::new();
hash_expression_recursive(expr, &mut hasher);
hasher.finish()
}
fn hash_expression_recursive<H: std::hash::Hasher>(expr: &Expression, hasher: &mut H) {
use std::hash::Hash;
match expr {
Expression::ColumnRef(col_id) => {
"col".hash(hasher);
col_id.table_canonical().hash(hasher);
col_id.column_canonical().hash(hasher);
}
Expression::Literal(val) => {
"lit".hash(hasher);
format!("{:?}", val).hash(hasher);
}
Expression::BinaryOp { left, op, right } => {
"binop".hash(hasher);
format!("{:?}", op).hash(hasher);
hash_expression_recursive(left, hasher);
hash_expression_recursive(right, hasher);
}
Expression::UnaryOp { op, expr: inner } => {
"unop".hash(hasher);
format!("{:?}", op).hash(hasher);
hash_expression_recursive(inner, hasher);
}
_ => {
format!("{:?}", expr).hash(hasher);
}
}
}
pub(super) fn find_cached_subexpression(
expr: &Expression,
expr_cache: &std::collections::HashMap<u64, usize>,
) -> Option<(usize, Expression)> {
if let Expression::BinaryOp { left, op: BinaryOperator::Multiply, right } = expr {
let left_hash = hash_expression(left);
if let Some(&cached_col) = expr_cache.get(&left_hash) {
return Some((cached_col, *right.clone()));
}
let right_hash = hash_expression(right);
if let Some(&cached_col) = expr_cache.get(&right_hash) {
return Some((cached_col, *left.clone()));
}
if let Some((cached_col, remaining)) = find_cached_subexpression(left, expr_cache) {
return Some((
cached_col,
Expression::BinaryOp {
left: Box::new(remaining),
op: BinaryOperator::Multiply,
right: right.clone(),
},
));
}
if let Some((cached_col, remaining)) = find_cached_subexpression(right, expr_cache) {
return Some((
cached_col,
Expression::BinaryOp {
left: left.clone(),
op: BinaryOperator::Multiply,
right: Box::new(remaining),
},
));
}
}
None
}