use vibesql_executor::SelectExecutor;
fn setup_test_database() -> vibesql_storage::Database {
let mut db = vibesql_storage::Database::new();
let schema = vibesql_catalog::TableSchema::new(
"SALES".to_string(),
vec![
vibesql_catalog::ColumnSchema::new(
"DEPT_ID".to_string(),
vibesql_types::DataType::Integer,
false,
),
vibesql_catalog::ColumnSchema::new(
"AMOUNT".to_string(),
vibesql_types::DataType::Integer,
false,
),
],
);
db.create_table(schema).unwrap();
db.insert_row(
"SALES",
vibesql_storage::Row::new(vec![
vibesql_types::SqlValue::Integer(1),
vibesql_types::SqlValue::Integer(150),
]),
)
.unwrap();
db.insert_row(
"SALES",
vibesql_storage::Row::new(vec![
vibesql_types::SqlValue::Integer(1),
vibesql_types::SqlValue::Integer(200),
]),
)
.unwrap();
db.insert_row(
"SALES",
vibesql_storage::Row::new(vec![
vibesql_types::SqlValue::Integer(2),
vibesql_types::SqlValue::Integer(50),
]),
)
.unwrap();
db.insert_row(
"SALES",
vibesql_storage::Row::new(vec![
vibesql_types::SqlValue::Integer(2),
vibesql_types::SqlValue::Integer(70),
]),
)
.unwrap();
db.insert_row(
"SALES",
vibesql_storage::Row::new(vec![
vibesql_types::SqlValue::Integer(3),
vibesql_types::SqlValue::Integer(120),
]),
)
.unwrap();
db
}
#[test]
fn test_having_aggregate_not_in_select() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let sql = "SELECT dept_id FROM sales GROUP BY dept_id HAVING AVG(amount) > 100";
println!("Test SQL: {}", sql);
let stmt = vibesql_parser::Parser::parse_sql(sql).unwrap();
let vibesql_ast::Statement::Select(select_stmt) = stmt else {
panic!("Expected SELECT statement");
};
let rows = executor.execute(&select_stmt).unwrap();
assert_eq!(rows.len(), 2, "Should return 2 rows (dept 1 and 3 have avg > 100)");
let dept_ids: Vec<i64> = rows
.iter()
.map(|r| match &r.values[0] {
vibesql_types::SqlValue::Integer(i) => *i,
_ => panic!("Expected integer value"),
})
.collect();
assert!(dept_ids.contains(&1), "Should contain dept 1");
assert!(dept_ids.contains(&3), "Should contain dept 3");
assert!(!dept_ids.contains(&2), "Should not contain dept 2");
}
#[test]
fn test_having_aggregate_also_in_select() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let sql =
"SELECT dept_id, AVG(amount) as avg_amt FROM sales GROUP BY dept_id HAVING AVG(amount) > 100";
println!("Test SQL: {}", sql);
let stmt = vibesql_parser::Parser::parse_sql(sql).unwrap();
let vibesql_ast::Statement::Select(select_stmt) = stmt else {
panic!("Expected SELECT statement");
};
let rows = executor.execute(&select_stmt).unwrap();
assert_eq!(rows.len(), 2, "Should return 2 rows (dept 1 and 3 have avg > 100)");
}
#[test]
fn test_having_different_aggregate_than_select() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let sql =
"SELECT dept_id, COUNT(*) as cnt FROM sales GROUP BY dept_id HAVING SUM(amount) > 100";
println!("Test SQL: {}", sql);
let stmt = vibesql_parser::Parser::parse_sql(sql).unwrap();
let vibesql_ast::Statement::Select(select_stmt) = stmt else {
panic!("Expected SELECT statement");
};
let rows = executor.execute(&select_stmt).unwrap();
assert_eq!(rows.len(), 3, "Should return 3 rows (all have sum > 100)");
}
#[test]
fn test_having_complex_expression_aggregate_not_in_select() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let sql = "SELECT dept_id FROM sales GROUP BY dept_id HAVING AVG(amount) * 2 > 200";
println!("Test SQL: {}", sql);
let stmt = vibesql_parser::Parser::parse_sql(sql).unwrap();
let vibesql_ast::Statement::Select(select_stmt) = stmt else {
panic!("Expected SELECT statement");
};
let rows = executor.execute(&select_stmt).unwrap();
assert_eq!(rows.len(), 2, "Should return 2 rows (dept 1 and 3 have avg*2 > 200)");
}
#[test]
fn test_having_count_star_not_in_select() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let sql = "SELECT dept_id FROM sales GROUP BY dept_id HAVING COUNT(*) > 1";
println!("Test SQL: {}", sql);
let stmt = vibesql_parser::Parser::parse_sql(sql).unwrap();
let vibesql_ast::Statement::Select(select_stmt) = stmt else {
panic!("Expected SELECT statement");
};
let rows = executor.execute(&select_stmt).unwrap();
assert_eq!(rows.len(), 2, "Should return 2 rows (dept 1 and 2 have count > 1)");
let dept_ids: Vec<i64> = rows
.iter()
.map(|r| match &r.values[0] {
vibesql_types::SqlValue::Integer(i) => *i,
_ => panic!("Expected integer value"),
})
.collect();
assert!(dept_ids.contains(&1), "Should contain dept 1");
assert!(dept_ids.contains(&2), "Should contain dept 2");
assert!(!dept_ids.contains(&3), "Should not contain dept 3");
}
#[test]
fn test_having_multiple_aggregates_not_in_select() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let sql =
"SELECT dept_id FROM sales GROUP BY dept_id HAVING AVG(amount) > 100 AND COUNT(*) > 1";
println!("Test SQL: {}", sql);
let stmt = vibesql_parser::Parser::parse_sql(sql).unwrap();
let vibesql_ast::Statement::Select(select_stmt) = stmt else {
panic!("Expected SELECT statement");
};
let rows = executor.execute(&select_stmt).unwrap();
assert_eq!(rows.len(), 1, "Should return 1 row (only dept 1 passes both conditions)");
let dept_id = match &rows[0].values[0] {
vibesql_types::SqlValue::Integer(i) => *i,
_ => panic!("Expected integer value"),
};
assert_eq!(dept_id, 1, "Should be dept 1");
}
#[test]
fn test_having_min_max_not_in_select() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let sql = "SELECT dept_id FROM sales GROUP BY dept_id HAVING MAX(amount) - MIN(amount) > 20";
println!("Test SQL: {}", sql);
let stmt = vibesql_parser::Parser::parse_sql(sql).unwrap();
let vibesql_ast::Statement::Select(select_stmt) = stmt else {
panic!("Expected SELECT statement");
};
let rows = executor.execute(&select_stmt).unwrap();
assert_eq!(rows.len(), 1, "Should return 1 row (only dept 1 has diff > 20)");
let dept_id = match &rows[0].values[0] {
vibesql_types::SqlValue::Integer(i) => *i,
_ => panic!("Expected integer value"),
};
assert_eq!(dept_id, 1, "Should be dept 1");
}
#[test]
fn test_having_sum_not_in_select() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let sql = "SELECT dept_id FROM sales GROUP BY dept_id HAVING SUM(amount) > 200";
println!("Test SQL: {}", sql);
let stmt = vibesql_parser::Parser::parse_sql(sql).unwrap();
let vibesql_ast::Statement::Select(select_stmt) = stmt else {
panic!("Expected SELECT statement");
};
let rows = executor.execute(&select_stmt).unwrap();
assert_eq!(rows.len(), 1, "Should return 1 row (only dept 1 has sum > 200)");
let dept_id = match &rows[0].values[0] {
vibesql_types::SqlValue::Integer(i) => *i,
_ => panic!("Expected integer value"),
};
assert_eq!(dept_id, 1, "Should be dept 1");
}
#[test]
fn test_having_without_group_by_aggregate_not_in_select() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let sql = "SELECT 1 as marker FROM sales HAVING COUNT(*) > 3";
println!("Test SQL: {}", sql);
let stmt = vibesql_parser::Parser::parse_sql(sql).unwrap();
let vibesql_ast::Statement::Select(select_stmt) = stmt else {
panic!("Expected SELECT statement");
};
let rows = executor.execute(&select_stmt).unwrap();
assert_eq!(rows.len(), 1, "Should return 1 row (count 5 > 3)");
}
#[test]
fn test_having_without_group_by_aggregate_in_both() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let sql = "SELECT COUNT(*) as cnt FROM sales HAVING COUNT(*) > 3";
println!("Test SQL: {}", sql);
let stmt = vibesql_parser::Parser::parse_sql(sql).unwrap();
let vibesql_ast::Statement::Select(select_stmt) = stmt else {
panic!("Expected SELECT statement");
};
let rows = executor.execute(&select_stmt).unwrap();
assert_eq!(rows.len(), 1, "Should return 1 row (count 5 > 3)");
let count = match &rows[0].values[0] {
vibesql_types::SqlValue::Integer(i) => *i,
_ => panic!("Expected integer value"),
};
assert_eq!(count, 5, "Should have count = 5");
}
#[test]
fn test_having_compare_two_aggregates() {
let mut db = vibesql_storage::Database::new();
let schema = vibesql_catalog::TableSchema::new(
"DATA".to_string(),
vec![
vibesql_catalog::ColumnSchema::new(
"GROUP_ID".to_string(),
vibesql_types::DataType::Integer,
false,
),
vibesql_catalog::ColumnSchema::new(
"VALUE_A".to_string(),
vibesql_types::DataType::Integer,
false,
),
vibesql_catalog::ColumnSchema::new(
"VALUE_B".to_string(),
vibesql_types::DataType::Integer,
false,
),
],
);
db.create_table(schema).unwrap();
db.insert_row(
"DATA",
vibesql_storage::Row::new(vec![
vibesql_types::SqlValue::Integer(1),
vibesql_types::SqlValue::Integer(200),
vibesql_types::SqlValue::Integer(50),
]),
)
.unwrap();
db.insert_row(
"DATA",
vibesql_storage::Row::new(vec![
vibesql_types::SqlValue::Integer(2),
vibesql_types::SqlValue::Integer(100),
vibesql_types::SqlValue::Integer(100),
]),
)
.unwrap();
let executor = SelectExecutor::new(&db);
let sql = "SELECT group_id FROM data GROUP BY group_id HAVING AVG(value_a) > AVG(value_b) * 2";
println!("Test SQL: {}", sql);
let stmt = vibesql_parser::Parser::parse_sql(sql).unwrap();
let vibesql_ast::Statement::Select(select_stmt) = stmt else {
panic!("Expected SELECT statement");
};
let rows = executor.execute(&select_stmt).unwrap();
assert_eq!(rows.len(), 1, "Should return 1 row (only group 1 passes)");
let group_id = match &rows[0].values[0] {
vibesql_types::SqlValue::Integer(i) => *i,
_ => panic!("Expected integer value"),
};
assert_eq!(group_id, 1, "Should be group 1");
}