use crate::database::Database;
use crate::error::SqawkError;
use crate::table::Table;
use crate::vm::bytecode::OpCode;
use crate::vm::compiler::SqlCompiler;
fn create_test_database() -> Database {
let mut database = Database::new();
let mut test_table = Table::new("users", vec![], None);
test_table.add_column("id".to_string(), "INTEGER".to_string());
test_table.add_column("age".to_string(), "INTEGER".to_string());
test_table.add_column("name".to_string(), "TEXT".to_string());
test_table
.add_row(vec![
crate::table::Value::Integer(1),
crate::table::Value::Integer(25),
crate::table::Value::String("Alice".to_string().into()),
])
.expect("Failed to add test row");
test_table
.add_row(vec![
crate::table::Value::Integer(2),
crate::table::Value::Integer(35),
crate::table::Value::String("Bob".to_string().into()),
])
.expect("Failed to add test row");
database
.add_table("users".to_string(), test_table)
.expect("Failed to add table");
database
}
#[test]
fn test_compile_select_with_where_gt() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, false);
let sql = "SELECT * FROM users WHERE age > 30";
let program = compiler.compile(sql).expect("Compilation failed");
let instructions = &program.instructions;
assert!(instructions.len() >= 8, "Expected at least 8 instructions");
let gt_found = instructions.iter().any(|inst| inst.opcode == OpCode::Gt);
let ifz_found = instructions.iter().any(|inst| inst.opcode == OpCode::IfZ);
let integer_found = instructions
.iter()
.any(|inst| inst.opcode == OpCode::Integer && inst.p1 == 30);
let halt_found = instructions.iter().any(|inst| inst.opcode == OpCode::Halt);
assert!(gt_found, "Expected Gt comparison instruction");
assert!(ifz_found, "Expected IfZ conditional jump instruction");
assert!(integer_found, "Expected Integer instruction with value 30");
assert!(halt_found, "Expected Halt instruction");
}
#[test]
fn test_compile_select_with_where_eq() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, true);
let sql = "SELECT * FROM users WHERE age = 25";
let program = compiler.compile(sql).expect("Compilation failed");
let instructions = &program.instructions;
let eq_found = instructions.iter().any(|inst| inst.opcode == OpCode::Eq);
let value_found = instructions
.iter()
.any(|inst| inst.opcode == OpCode::Integer && inst.p1 == 25);
assert!(eq_found, "Expected Eq comparison instruction");
assert!(value_found, "Expected Integer instruction with value 25");
}
#[test]
fn test_compile_select_with_where_string() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, true);
let sql = "SELECT * FROM users WHERE name = 'Alice'";
let program = compiler.compile(sql).expect("Compilation failed");
let instructions = &program.instructions;
let eq_found = instructions.iter().any(|inst| inst.opcode == OpCode::Eq);
let string_found = instructions
.iter()
.any(|inst| inst.opcode == OpCode::String && inst.p4.as_deref() == Some("Alice"));
assert!(eq_found, "Expected Eq comparison instruction");
assert!(
string_found,
"Expected String instruction with value 'Alice'"
);
}
#[test]
fn test_compile_select_with_where_lt() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, true);
let sql = "SELECT * FROM users WHERE age < 30";
let program = compiler.compile(sql).expect("Compilation failed");
let instructions = &program.instructions;
let lt_found = instructions.iter().any(|inst| inst.opcode == OpCode::Lt);
assert!(lt_found, "Expected Lt comparison instruction");
}
#[test]
fn test_compile_select_without_where() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, true);
let sql = "SELECT * FROM users";
let program = compiler.compile(sql).expect("Compilation failed");
let instructions = &program.instructions;
let comparison_found = instructions.iter().any(|inst| {
matches!(
inst.opcode,
OpCode::Gt | OpCode::Lt | OpCode::Eq | OpCode::Ne | OpCode::Ge | OpCode::Le
)
});
let ifz_found = instructions.iter().any(|inst| inst.opcode == OpCode::IfZ);
assert!(
!comparison_found,
"Should not have comparison instructions without WHERE"
);
assert!(!ifz_found, "Should not have conditional jump without WHERE");
}
#[test]
fn test_column_not_found_error() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, false);
let sql = "SELECT * FROM users WHERE invalid_column > 10";
let result = compiler.compile(sql);
assert!(result.is_err(), "Expected compilation to fail");
match result.unwrap_err() {
SqawkError::ColumnNotFound(col) => {
assert_eq!(col, "invalid_column");
}
other => panic!("Expected ColumnNotFound error, got {:?}", other),
}
}
#[test]
fn test_in_subquery_compilation() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, false);
let sql = "SELECT * FROM users WHERE age IN (SELECT age FROM users)";
let result = compiler.compile(sql);
assert!(
result.is_ok(),
"Expected compilation to succeed: {:?}",
result.unwrap_err()
);
}
#[test]
fn test_between_compilation() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, true);
let sql = "SELECT * FROM users WHERE age BETWEEN 20 AND 30";
let program = compiler.compile(sql).expect("BETWEEN compilation failed");
let instructions = &program.instructions;
let ge_found = instructions.iter().any(|inst| inst.opcode == OpCode::Ge);
let le_found = instructions.iter().any(|inst| inst.opcode == OpCode::Le);
assert!(ge_found, "Expected Ge comparison instruction for BETWEEN");
assert!(le_found, "Expected Le comparison instruction for BETWEEN");
}
#[test]
fn test_in_list_compilation() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, true);
let sql = "SELECT * FROM users WHERE age IN (25, 30, 35)";
let program = compiler.compile(sql).expect("IN list compilation failed");
let instructions = &program.instructions;
let eq_count = instructions
.iter()
.filter(|inst| inst.opcode == OpCode::Eq)
.count();
assert!(
eq_count >= 3,
"Expected at least 3 Eq comparison instructions for IN list"
);
}
#[test]
fn test_like_pattern_compilation() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, true);
let sql = "SELECT * FROM users WHERE name LIKE 'A%'";
let program = compiler.compile(sql).expect("LIKE compilation failed");
let instructions = &program.instructions;
let like_found = instructions
.iter()
.any(|inst| inst.opcode == OpCode::Like && inst.p4.as_deref() == Some("A%"));
assert!(like_found, "Expected Like instruction with pattern 'A%'");
}
#[test]
fn test_ilike_pattern_compilation() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, true);
let sql = "SELECT * FROM users WHERE name ILIKE 'alice'";
let program = compiler.compile(sql).expect("ILIKE compilation failed");
let instructions = &program.instructions;
let ilike_found = instructions.iter().any(|inst| {
inst.opcode == OpCode::Like && inst.p2 == 2 });
assert!(
ilike_found,
"Expected Like instruction with case-insensitive flag"
);
}
#[test]
fn test_case_compilation() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, true);
let sql = "SELECT * FROM users WHERE CASE WHEN age > 30 THEN 1 ELSE 0 END = 1";
let program = compiler.compile(sql).expect("CASE compilation failed");
let instructions = &program.instructions;
let gt_found = instructions.iter().any(|inst| inst.opcode == OpCode::Gt);
let ifz_found = instructions.iter().any(|inst| inst.opcode == OpCode::IfZ);
let goto_found = instructions.iter().any(|inst| inst.opcode == OpCode::Goto);
assert!(
gt_found,
"Expected Gt comparison instruction for CASE WHEN condition"
);
assert!(
ifz_found,
"Expected IfZ conditional jump instruction for CASE"
);
assert!(
goto_found,
"Expected Goto instruction for CASE branch jumps"
);
}
#[test]
fn test_cast_compilation() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, true);
let sql = "SELECT * FROM users WHERE CAST(age AS TEXT) = '25'";
let program = compiler.compile(sql).expect("CAST compilation failed");
let instructions = &program.instructions;
let cast_found = instructions
.iter()
.any(|inst| inst.opcode == OpCode::Cast && inst.p4.as_deref() == Some("TEXT"));
assert!(cast_found, "Expected Cast instruction with TEXT type");
}
#[test]
fn test_coalesce_compilation() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, true);
let sql = "SELECT * FROM users WHERE COALESCE(name, 'default') = 'Alice'";
let program = compiler.compile(sql).expect("COALESCE compilation failed");
let instructions = &program.instructions;
let is_null_found = instructions
.iter()
.any(|inst| inst.opcode == OpCode::IsNull);
assert!(is_null_found, "Expected IsNull instruction for COALESCE");
}
#[test]
fn test_nullif_compilation() {
let database = create_test_database();
let mut compiler = SqlCompiler::new(&database, true);
let sql = "SELECT * FROM users WHERE NULLIF(name, 'Alice') IS NULL";
let program = compiler.compile(sql).expect("NULLIF compilation failed");
let instructions = &program.instructions;
let eq_found = instructions.iter().any(|inst| inst.opcode == OpCode::Eq);
let null_found = instructions.iter().any(|inst| inst.opcode == OpCode::Null);
assert!(eq_found, "Expected Eq instruction for NULLIF comparison");
assert!(null_found, "Expected Null instruction for NULLIF result");
}