use vibesql_parser::Parser;
use vibesql_storage::Database;
use crate::{CreateTableExecutor, InsertExecutor, SelectExecutor};
fn exec_sql(db: &mut Database, sql: &str) {
let stmt = Parser::parse_sql(sql).unwrap();
match stmt {
vibesql_ast::Statement::CreateTable(s) => {
CreateTableExecutor::execute(&s, db).unwrap();
}
vibesql_ast::Statement::Insert(s) => {
InsertExecutor::execute(db, &s).unwrap();
}
_ => panic!("Unexpected statement type in test"),
}
}
#[test]
fn test_cross_join_no_condition() {
let mut db = Database::new();
exec_sql(&mut db, "CREATE TABLE t1 (a INTEGER)");
exec_sql(&mut db, "INSERT INTO t1 VALUES (1)");
exec_sql(&mut db, "INSERT INTO t1 VALUES (2)");
exec_sql(&mut db, "INSERT INTO t1 VALUES (3)");
exec_sql(&mut db, "CREATE TABLE t2 (b INTEGER)");
exec_sql(&mut db, "INSERT INTO t2 VALUES (10)");
exec_sql(&mut db, "INSERT INTO t2 VALUES (20)");
let executor = SelectExecutor::new(&db);
let select_stmt = Parser::parse_sql("SELECT * FROM t1, t2").unwrap();
let result = if let vibesql_ast::Statement::Select(s) = select_stmt {
executor.execute(&s).unwrap()
} else {
panic!("Expected SELECT statement")
};
assert_eq!(result.len(), 6);
}
#[test]
fn test_cross_join_with_single_table_predicates() {
let mut db = Database::new();
exec_sql(&mut db, "CREATE TABLE t1 (a INTEGER)");
for i in 1..=10 {
exec_sql(&mut db, &format!("INSERT INTO t1 VALUES ({})", i));
}
exec_sql(&mut db, "CREATE TABLE t2 (b INTEGER)");
for i in 1..=10 {
exec_sql(&mut db, &format!("INSERT INTO t2 VALUES ({})", i));
}
let executor = SelectExecutor::new(&db);
let select_stmt = Parser::parse_sql("SELECT * FROM t1, t2 WHERE a < 4 AND b > 7").unwrap();
let result = if let vibesql_ast::Statement::Select(s) = select_stmt {
executor.execute(&s).unwrap()
} else {
panic!("Expected SELECT statement")
};
assert_eq!(result.len(), 9);
for row in &result {
let a = match &row.values[0] {
vibesql_types::SqlValue::Integer(i) => *i,
_ => panic!("Expected integer"),
};
let b = match &row.values[1] {
vibesql_types::SqlValue::Integer(i) => *i,
_ => panic!("Expected integer"),
};
assert!(a < 4, "a={} should be < 4", a);
assert!(b > 7, "b={} should be > 7", b);
}
}
#[test]
fn test_multi_table_cross_join_with_single_table_predicates() {
let mut db = Database::new();
exec_sql(&mut db, "CREATE TABLE t1 (a1 INTEGER)");
exec_sql(&mut db, "CREATE TABLE t2 (a2 INTEGER)");
exec_sql(&mut db, "CREATE TABLE t3 (a3 INTEGER)");
exec_sql(&mut db, "CREATE TABLE t4 (a4 INTEGER)");
for i in 1..=10 {
exec_sql(&mut db, &format!("INSERT INTO t1 VALUES ({})", i));
exec_sql(&mut db, &format!("INSERT INTO t2 VALUES ({})", i));
exec_sql(&mut db, &format!("INSERT INTO t3 VALUES ({})", i));
exec_sql(&mut db, &format!("INSERT INTO t4 VALUES ({})", i));
}
let executor = SelectExecutor::new(&db);
let select_stmt = Parser::parse_sql(
"SELECT * FROM t1, t2, t3, t4 WHERE a1 IN (1,2) AND a2 IN (3,4) AND a3 IN (5,6) AND a4 IN (7,8)",
)
.unwrap();
let result = if let vibesql_ast::Statement::Select(s) = select_stmt {
executor.execute(&s).unwrap()
} else {
panic!("Expected SELECT statement")
};
assert_eq!(result.len(), 16);
}
#[test]
fn test_cross_join_empty_table() {
let mut db = Database::new();
exec_sql(&mut db, "CREATE TABLE t1 (a INTEGER)");
exec_sql(&mut db, "INSERT INTO t1 VALUES (1)");
exec_sql(&mut db, "INSERT INTO t1 VALUES (2)");
exec_sql(&mut db, "CREATE TABLE t2 (b INTEGER)");
let executor = SelectExecutor::new(&db);
let select_stmt = Parser::parse_sql("SELECT * FROM t1, t2").unwrap();
let result = if let vibesql_ast::Statement::Select(s) = select_stmt {
executor.execute(&s).unwrap()
} else {
panic!("Expected SELECT statement")
};
assert_eq!(result.len(), 0);
}
#[test]
fn test_cross_join_single_row_tables() {
let mut db = Database::new();
exec_sql(&mut db, "CREATE TABLE t1 (a INTEGER)");
exec_sql(&mut db, "INSERT INTO t1 VALUES (1)");
exec_sql(&mut db, "CREATE TABLE t2 (b INTEGER)");
exec_sql(&mut db, "INSERT INTO t2 VALUES (2)");
exec_sql(&mut db, "CREATE TABLE t3 (c INTEGER)");
exec_sql(&mut db, "INSERT INTO t3 VALUES (3)");
let executor = SelectExecutor::new(&db);
let select_stmt = Parser::parse_sql("SELECT * FROM t1, t2, t3").unwrap();
let result = if let vibesql_ast::Statement::Select(s) = select_stmt {
executor.execute(&s).unwrap()
} else {
panic!("Expected SELECT statement")
};
assert_eq!(result.len(), 1);
assert_eq!(result[0].values.len(), 3);
}
#[test]
fn test_cross_join_with_in_predicates() {
let mut db = Database::new();
exec_sql(&mut db, "CREATE TABLE t1 (e INTEGER)");
for i in 1..=20 {
exec_sql(&mut db, &format!("INSERT INTO t1 VALUES ({})", i));
}
exec_sql(&mut db, "CREATE TABLE t2 (f INTEGER)");
for i in 1..=20 {
exec_sql(&mut db, &format!("INSERT INTO t2 VALUES ({})", i));
}
let executor = SelectExecutor::new(&db);
let select_stmt =
Parser::parse_sql("SELECT * FROM t1, t2 WHERE e IN (1,5,10) AND f IN (2,4,6,8)").unwrap();
let result = if let vibesql_ast::Statement::Select(s) = select_stmt {
executor.execute(&s).unwrap()
} else {
panic!("Expected SELECT statement")
};
assert_eq!(result.len(), 12);
}
#[test]
fn test_cross_join_with_or_predicates() {
let mut db = Database::new();
exec_sql(&mut db, "CREATE TABLE t1 (a INTEGER)");
for i in 1..=10 {
exec_sql(&mut db, &format!("INSERT INTO t1 VALUES ({})", i));
}
exec_sql(&mut db, "CREATE TABLE t2 (b INTEGER)");
for i in 1..=10 {
exec_sql(&mut db, &format!("INSERT INTO t2 VALUES ({})", i));
}
let executor = SelectExecutor::new(&db);
let select_stmt =
Parser::parse_sql("SELECT * FROM t1, t2 WHERE (a=1 OR a=5) AND (b=2 OR b=8)").unwrap();
let result = if let vibesql_ast::Statement::Select(s) = select_stmt {
executor.execute(&s).unwrap()
} else {
panic!("Expected SELECT statement")
};
assert_eq!(result.len(), 4);
}