mod alias_resolution;
mod expansion;
mod expression_utils;
pub use alias_resolution::{
resolve_base_expressions_aliases, resolve_group_by_alias, resolve_grouping_set_aliases,
resolve_having_aliases_with_values,
};
pub use expansion::{expand_group_by_clause, get_base_expressions};
pub use expression_utils::expressions_equal;
use vibesql_ast::Expression;
#[derive(Debug, Clone)]
pub struct ResolvedGroupingSet {
pub group_by_exprs: Vec<Expression>,
pub rolled_up: Vec<bool>,
}
#[derive(Debug, Clone, Default)]
pub struct GroupingContext {
pub base_expressions: Vec<Expression>,
pub rolled_up: Vec<bool>,
}
impl GroupingContext {
pub fn is_rolled_up(&self, expr: &Expression) -> i32 {
for (i, base_expr) in self.base_expressions.iter().enumerate() {
if expressions_equal(expr, base_expr) {
return if self.rolled_up.get(i).copied().unwrap_or(false) { 1 } else { 0 };
}
}
0
}
pub fn grouping_id(&self, exprs: &[Expression]) -> i64 {
let n = exprs.len();
let mut result: i64 = 0;
for (i, expr) in exprs.iter().enumerate() {
let is_rolled_up = self.is_rolled_up(expr);
if is_rolled_up == 1 {
result |= 1i64 << (n - 1 - i);
}
}
result
}
}
#[cfg(test)]
mod tests {
use vibesql_ast::BinaryOperator;
use super::*;
fn col(name: &str) -> Expression {
Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(&name, false))
}
fn lit_int(n: i64) -> Expression {
Expression::Literal(vibesql_types::SqlValue::Integer(n))
}
fn binary_op(op: BinaryOperator, left: Expression, right: Expression) -> Expression {
Expression::BinaryOp { op, left: Box::new(left), right: Box::new(right) }
}
#[test]
fn test_grouping_context() {
let ctx = GroupingContext {
base_expressions: vec![col("a"), col("b"), col("c")],
rolled_up: vec![false, true, true],
};
assert_eq!(ctx.is_rolled_up(&col("a")), 0); assert_eq!(ctx.is_rolled_up(&col("b")), 1); assert_eq!(ctx.is_rolled_up(&col("c")), 1); assert_eq!(ctx.is_rolled_up(&col("d")), 0); }
#[test]
fn test_grouping_id() {
let ctx = GroupingContext {
base_expressions: vec![col("a"), col("b"), col("c")],
rolled_up: vec![false, false, false], };
assert_eq!(ctx.grouping_id(&[col("a"), col("b"), col("c")]), 0);
let ctx = GroupingContext {
base_expressions: vec![col("a"), col("b"), col("c")],
rolled_up: vec![false, false, true], };
assert_eq!(ctx.grouping_id(&[col("a"), col("b"), col("c")]), 1);
let ctx = GroupingContext {
base_expressions: vec![col("a"), col("b"), col("c")],
rolled_up: vec![false, true, true], };
assert_eq!(ctx.grouping_id(&[col("a"), col("b"), col("c")]), 3);
let ctx = GroupingContext {
base_expressions: vec![col("a"), col("b"), col("c")],
rolled_up: vec![true, true, true], };
assert_eq!(ctx.grouping_id(&[col("a"), col("b"), col("c")]), 7);
let ctx = GroupingContext {
base_expressions: vec![col("a"), col("b"), col("c")],
rolled_up: vec![true, false, true], };
assert_eq!(ctx.grouping_id(&[col("a"), col("c")]), 3);
assert_eq!(ctx.grouping_id(&[col("b"), col("c")]), 1);
}
#[test]
fn test_grouping_id_single_column() {
let ctx = GroupingContext { base_expressions: vec![col("a")], rolled_up: vec![false] };
assert_eq!(ctx.grouping_id(&[col("a")]), 0);
let ctx = GroupingContext { base_expressions: vec![col("a")], rolled_up: vec![true] };
assert_eq!(ctx.grouping_id(&[col("a")]), 1);
}
#[test]
fn test_grouping_context_with_expressions() {
let expr = binary_op(BinaryOperator::Plus, col("a"), lit_int(1));
let ctx = GroupingContext {
base_expressions: vec![col("a"), expr.clone()],
rolled_up: vec![false, true],
};
assert_eq!(ctx.is_rolled_up(&col("a")), 0);
assert_eq!(ctx.is_rolled_up(&col("A")), 0);
assert_eq!(ctx.is_rolled_up(&expr), 1);
let expr_upper = binary_op(BinaryOperator::Plus, col("A"), lit_int(1));
assert_eq!(ctx.is_rolled_up(&expr_upper), 1);
}
}