use vibesql_executor::SelectExecutor;
fn setup_test_database() -> vibesql_storage::Database {
let mut db = vibesql_storage::Database::new();
let sales_schema = vibesql_catalog::TableSchema::new(
"SALES".to_string(),
vec![
vibesql_catalog::ColumnSchema::new(
"ID".to_string(),
vibesql_types::DataType::Integer,
false,
),
vibesql_catalog::ColumnSchema::new(
"DEPT".to_string(),
vibesql_types::DataType::Integer,
false,
),
vibesql_catalog::ColumnSchema::new(
"AMOUNT".to_string(),
vibesql_types::DataType::Integer,
false,
),
],
);
db.create_table(sales_schema).unwrap();
db.insert_row(
"SALES",
vibesql_storage::Row::new(vec![
vibesql_types::SqlValue::Integer(1),
vibesql_types::SqlValue::Integer(1), vibesql_types::SqlValue::Integer(100),
]),
)
.unwrap();
db.insert_row(
"SALES",
vibesql_storage::Row::new(vec![
vibesql_types::SqlValue::Integer(2),
vibesql_types::SqlValue::Integer(1), vibesql_types::SqlValue::Integer(200),
]),
)
.unwrap();
db.insert_row(
"SALES",
vibesql_storage::Row::new(vec![
vibesql_types::SqlValue::Integer(3),
vibesql_types::SqlValue::Integer(2), vibesql_types::SqlValue::Integer(50),
]),
)
.unwrap();
db.insert_row(
"SALES",
vibesql_storage::Row::new(vec![
vibesql_types::SqlValue::Integer(4),
vibesql_types::SqlValue::Integer(2), vibesql_types::SqlValue::Integer(75),
]),
)
.unwrap();
db.insert_row(
"SALES",
vibesql_storage::Row::new(vec![
vibesql_types::SqlValue::Integer(5),
vibesql_types::SqlValue::Integer(3), vibesql_types::SqlValue::Integer(500),
]),
)
.unwrap();
let targets_schema = vibesql_catalog::TableSchema::new(
"TARGETS".to_string(),
vec![
vibesql_catalog::ColumnSchema::new(
"ID".to_string(),
vibesql_types::DataType::Integer,
false,
),
vibesql_catalog::ColumnSchema::new(
"TARGET_AMOUNT".to_string(),
vibesql_types::DataType::Integer,
false,
),
],
);
db.create_table(targets_schema).unwrap();
db.insert_row(
"TARGETS",
vibesql_storage::Row::new(vec![
vibesql_types::SqlValue::Integer(1),
vibesql_types::SqlValue::Integer(200),
]),
)
.unwrap();
db
}
#[test]
fn test_having_with_scalar_subquery() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let stmt = vibesql_parser::Parser::parse_sql(
"SELECT dept, SUM(amount) as total \
FROM sales \
GROUP BY dept \
HAVING SUM(amount) > (SELECT AVG(target_amount) FROM targets) \
ORDER BY dept",
)
.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);
assert_eq!(rows[0].get(0), Some(&vibesql_types::SqlValue::Integer(1)));
assert_eq!(rows[0].get(1), Some(&vibesql_types::SqlValue::Integer(300)));
assert_eq!(rows[1].get(0), Some(&vibesql_types::SqlValue::Integer(3)));
assert_eq!(rows[1].get(1), Some(&vibesql_types::SqlValue::Integer(500)))
}
#[test]
fn test_having_with_scalar_subquery_comparison() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let stmt = vibesql_parser::Parser::parse_sql(
"SELECT dept, COUNT(*) as cnt \
FROM sales \
GROUP BY dept \
HAVING COUNT(*) < (SELECT MAX(target_amount) FROM targets)",
)
.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);
}
#[test]
fn test_having_with_exists_subquery() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let stmt = vibesql_parser::Parser::parse_sql(
"SELECT dept, SUM(amount) as total \
FROM sales \
GROUP BY dept \
HAVING EXISTS (SELECT 1 FROM targets)",
)
.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);
}
#[test]
fn test_having_with_not_exists_subquery() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let stmt = vibesql_parser::Parser::parse_sql(
"SELECT dept, SUM(amount) as total \
FROM sales \
GROUP BY dept \
HAVING NOT EXISTS (SELECT 1 FROM targets WHERE target_amount > 1000)",
)
.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);
}
#[test]
fn test_having_with_in_subquery() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let stmt = vibesql_parser::Parser::parse_sql(
"SELECT dept, SUM(amount) as total \
FROM sales \
GROUP BY dept \
HAVING SUM(amount) IN (SELECT target_amount FROM targets)",
)
.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(), 0);
}
#[test]
fn test_having_with_not_in_subquery() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let stmt = vibesql_parser::Parser::parse_sql(
"SELECT dept, SUM(amount) as total \
FROM sales \
GROUP BY dept \
HAVING SUM(amount) NOT IN (SELECT target_amount FROM targets)",
)
.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);
}
#[test]
fn test_having_with_complex_boolean_expression() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let stmt = vibesql_parser::Parser::parse_sql(
"SELECT dept, SUM(amount) as total \
FROM sales \
GROUP BY dept \
HAVING SUM(amount) > (SELECT AVG(target_amount) FROM targets) \
AND COUNT(*) >= 1",
)
.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);
}
#[test]
fn test_having_without_group_by_with_subquery() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let stmt = vibesql_parser::Parser::parse_sql(
"SELECT COUNT(*) as cnt \
FROM sales \
HAVING COUNT(*) > (SELECT COUNT(*) FROM targets)",
)
.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);
assert_eq!(rows[0].get(0), Some(&vibesql_types::SqlValue::Integer(5)));
}
#[test]
fn test_having_with_quantified_all() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let stmt = vibesql_parser::Parser::parse_sql(
"SELECT dept, SUM(amount) as total \
FROM sales \
GROUP BY dept \
HAVING SUM(amount) > ALL (SELECT target_amount FROM targets)",
)
.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);
}
#[test]
fn test_having_with_quantified_any() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let stmt = vibesql_parser::Parser::parse_sql(
"SELECT dept, SUM(amount) as total \
FROM sales \
GROUP BY dept \
HAVING SUM(amount) < ANY (SELECT target_amount FROM targets)",
)
.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);
assert_eq!(rows[0].get(0), Some(&vibesql_types::SqlValue::Integer(2)));
}
#[test]
fn test_having_subquery_returns_empty() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let stmt = vibesql_parser::Parser::parse_sql(
"SELECT dept, SUM(amount) as total \
FROM sales \
GROUP BY dept \
HAVING SUM(amount) > (SELECT target_amount FROM targets WHERE target_amount > 1000)",
)
.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(), 0);
}
#[test]
fn test_having_with_multiple_aggregates_and_subquery() {
let db = setup_test_database();
let executor = SelectExecutor::new(&db);
let stmt = vibesql_parser::Parser::parse_sql(
"SELECT dept, SUM(amount) as total, AVG(amount) as avg_amt \
FROM sales \
GROUP BY dept \
HAVING SUM(amount) > (SELECT AVG(target_amount) FROM targets) \
AND AVG(amount) > 100",
)
.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);
}