use vibesql_ast::{IndexColumn, OrderDirection};
use vibesql_catalog::{ColumnSchema, TableSchema};
use vibesql_parser::Parser;
use vibesql_storage::{Database, Row};
use vibesql_types::{DataType, SqlValue};
use crate::select::SelectExecutor;
fn create_employees_table() -> Database {
let mut db = Database::new();
db.catalog.set_case_sensitive_identifiers(false);
let schema = TableSchema::new(
"employees".to_string(),
vec![
ColumnSchema::new("id".to_string(), DataType::Integer, false),
ColumnSchema::new(
"department".to_string(),
DataType::Varchar { max_length: Some(50) },
false,
),
ColumnSchema::new("salary".to_string(), DataType::Integer, false),
ColumnSchema::new(
"name".to_string(),
DataType::Varchar { max_length: Some(100) },
false,
),
],
);
db.create_table(schema).unwrap();
db
}
#[test]
fn test_basic_non_unique_index_with_duplicates() {
let mut db = create_employees_table();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(100000),
SqlValue::Varchar(arcstr::ArcStr::from("Alice")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(2),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(120000),
SqlValue::Varchar(arcstr::ArcStr::from("Bob")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(3),
SqlValue::Varchar(arcstr::ArcStr::from("Sales")),
SqlValue::Integer(80000),
SqlValue::Varchar(arcstr::ArcStr::from("Charlie")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(4),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(110000),
SqlValue::Varchar(arcstr::ArcStr::from("Diana")),
]),
)
.unwrap();
db.create_index(
"idx_dept".to_string(),
"employees".to_string(),
false, vec![IndexColumn::Column {
column_name: "department".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT * FROM employees WHERE department = 'Engineering'";
let stmt = Parser::parse_sql(query).unwrap();
if let vibesql_ast::Statement::Select(select_stmt) = stmt {
let result = executor.execute(&select_stmt).unwrap();
assert_eq!(result.len(), 3, "Should return all 3 Engineering employees");
let ids: Vec<i64> = result
.iter()
.map(|row| match &row.values[0] {
SqlValue::Integer(id) => *id,
_ => panic!("Expected integer ID"),
})
.collect();
assert!(ids.contains(&1)); assert!(ids.contains(&2)); assert!(ids.contains(&4)); } else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_multi_column_non_unique_index() {
let mut db = create_employees_table();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(100000),
SqlValue::Varchar(arcstr::ArcStr::from("Alice")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(2),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(120000),
SqlValue::Varchar(arcstr::ArcStr::from("Bob")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(3),
SqlValue::Varchar(arcstr::ArcStr::from("Sales")),
SqlValue::Integer(80000),
SqlValue::Varchar(arcstr::ArcStr::from("Charlie")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(5),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(100000),
SqlValue::Varchar(arcstr::ArcStr::from("Eve")),
]),
)
.unwrap();
db.create_index(
"idx_dept_salary".to_string(),
"employees".to_string(),
false, vec![
IndexColumn::Column {
column_name: "department".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
},
IndexColumn::Column {
column_name: "salary".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
},
],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT * FROM employees WHERE department = 'Engineering' AND salary = 100000";
let stmt = Parser::parse_sql(query).unwrap();
if let vibesql_ast::Statement::Select(select_stmt) = stmt {
let result = executor.execute(&select_stmt).unwrap();
assert_eq!(result.len(), 2, "Should return 2 employees (Alice and Eve)");
let ids: Vec<i64> = result
.iter()
.map(|row| match &row.values[0] {
SqlValue::Integer(id) => *id,
_ => panic!("Expected integer ID"),
})
.collect();
assert!(ids.contains(&1)); assert!(ids.contains(&5)); } else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_range_queries_with_duplicates() {
let mut db = create_employees_table();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(100000),
SqlValue::Varchar(arcstr::ArcStr::from("Alice")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(2),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(120000),
SqlValue::Varchar(arcstr::ArcStr::from("Bob")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(3),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(110000),
SqlValue::Varchar(arcstr::ArcStr::from("Charlie")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(4),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(100000),
SqlValue::Varchar(arcstr::ArcStr::from("Diana")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(5),
SqlValue::Varchar(arcstr::ArcStr::from("Sales")),
SqlValue::Integer(90000),
SqlValue::Varchar(arcstr::ArcStr::from("Eve")),
]),
)
.unwrap();
db.create_index(
"idx_salary".to_string(),
"employees".to_string(),
false, vec![IndexColumn::Column {
column_name: "salary".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query =
"SELECT * FROM employees WHERE salary >= 100000 AND salary <= 120000 ORDER BY salary, id";
let stmt = Parser::parse_sql(query).unwrap();
if let vibesql_ast::Statement::Select(select_stmt) = stmt {
let result = executor.execute(&select_stmt).unwrap();
assert_eq!(result.len(), 4, "Should return 4 employees in salary range");
let ids: Vec<i64> = result
.iter()
.map(|row| match &row.values[0] {
SqlValue::Integer(id) => *id,
_ => panic!("Expected integer ID"),
})
.collect();
assert!(ids.contains(&1)); assert!(ids.contains(&4)); assert!(ids.contains(&3)); assert!(ids.contains(&2)); } else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_updates_affecting_non_unique_indexes() {
let mut db = create_employees_table();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(100000),
SqlValue::Varchar(arcstr::ArcStr::from("Alice")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(2),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(120000),
SqlValue::Varchar(arcstr::ArcStr::from("Bob")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(3),
SqlValue::Varchar(arcstr::ArcStr::from("Sales")),
SqlValue::Integer(80000),
SqlValue::Varchar(arcstr::ArcStr::from("Charlie")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(4),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(110000),
SqlValue::Varchar(arcstr::ArcStr::from("Diana")),
]),
)
.unwrap();
db.create_index(
"idx_dept".to_string(),
"employees".to_string(),
false, vec![IndexColumn::Column {
column_name: "department".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let update_query = "UPDATE employees SET department = 'Marketing' WHERE id = 2";
let update_stmt = Parser::parse_sql(update_query).unwrap();
if let vibesql_ast::Statement::Update(stmt) = update_stmt {
let updated = crate::update::UpdateExecutor::execute(&stmt, &mut db).unwrap();
assert_eq!(updated, 1, "Should update 1 row");
} else {
panic!("Expected UPDATE statement");
}
let executor = SelectExecutor::new(&db);
let query = "SELECT * FROM employees WHERE department = 'Engineering'";
let stmt = Parser::parse_sql(query).unwrap();
if let vibesql_ast::Statement::Select(select_stmt) = stmt {
let result = executor.execute(&select_stmt).unwrap();
assert_eq!(result.len(), 2, "Should return 2 Engineering employees after update");
let ids: Vec<i64> = result
.iter()
.map(|row| match &row.values[0] {
SqlValue::Integer(id) => *id,
_ => panic!("Expected integer ID"),
})
.collect();
assert!(ids.contains(&1)); assert!(ids.contains(&4)); assert!(!ids.contains(&2)); } else {
panic!("Expected SELECT statement");
}
let query2 = "SELECT * FROM employees WHERE department = 'Marketing'";
let stmt2 = Parser::parse_sql(query2).unwrap();
if let vibesql_ast::Statement::Select(select_stmt) = stmt2 {
let result = executor.execute(&select_stmt).unwrap();
assert_eq!(result.len(), 1, "Should return 1 Marketing employee");
assert_eq!(result[0].values[0], SqlValue::Integer(2)); } else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_deletes_with_duplicates() {
let mut db = create_employees_table();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(100000),
SqlValue::Varchar(arcstr::ArcStr::from("Alice")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(2),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(120000),
SqlValue::Varchar(arcstr::ArcStr::from("Bob")),
]),
)
.unwrap();
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(4),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(110000),
SqlValue::Varchar(arcstr::ArcStr::from("Diana")),
]),
)
.unwrap();
db.create_index(
"idx_dept".to_string(),
"employees".to_string(),
false, vec![IndexColumn::Column {
column_name: "department".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let delete_query = "DELETE FROM employees WHERE id = 1";
let delete_stmt = Parser::parse_sql(delete_query).unwrap();
if let vibesql_ast::Statement::Delete(stmt) = delete_stmt {
let deleted = crate::delete::DeleteExecutor::execute(&stmt, &mut db).unwrap();
assert_eq!(deleted, 1, "Should delete 1 row");
} else {
panic!("Expected DELETE statement");
}
let executor = SelectExecutor::new(&db);
let query = "SELECT * FROM employees WHERE department = 'Engineering'";
let stmt = Parser::parse_sql(query).unwrap();
if let vibesql_ast::Statement::Select(select_stmt) = stmt {
let result = executor.execute(&select_stmt).unwrap();
assert_eq!(result.len(), 2, "Should return 2 Engineering employees after delete");
let ids: Vec<i64> = result
.iter()
.map(|row| match &row.values[0] {
SqlValue::Integer(id) => *id,
_ => panic!("Expected integer ID"),
})
.collect();
assert!(!ids.contains(&1)); assert!(ids.contains(&2)); assert!(ids.contains(&4)); } else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_heavy_duplicate_scenario_in_memory() {
let mut db = create_employees_table();
for i in 0..1000 {
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(i as i64),
SqlValue::Varchar(arcstr::ArcStr::from("Engineering")),
SqlValue::Integer(100000),
SqlValue::Varchar(arcstr::ArcStr::from(format!("Employee{}", i))),
]),
)
.unwrap();
}
db.create_index(
"idx_dept".to_string(),
"employees".to_string(),
false, vec![IndexColumn::Column {
column_name: "department".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT COUNT(*) FROM employees WHERE department = 'Engineering'";
let stmt = Parser::parse_sql(query).unwrap();
if let vibesql_ast::Statement::Select(select_stmt) = stmt {
let result = executor.execute(&select_stmt).unwrap();
assert_eq!(result.len(), 1, "COUNT should return 1 row");
match &result[0].values[0] {
SqlValue::Integer(count) => {
assert_eq!(*count, 1000, "Should return count of 1000 employees");
}
_ => panic!("Expected integer count"),
}
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_heavy_duplicate_scenario_disk_backed() {
let mut db = create_employees_table();
for i in 0..5_025 {
let dept = match i % 3 {
0 => "Engineering",
1 => "Sales",
_ => "Marketing",
};
db.insert_row(
"employees",
Row::new(vec![
SqlValue::Integer(i as i64),
SqlValue::Varchar(arcstr::ArcStr::from(dept)),
SqlValue::Integer(80000 + (i % 50000) as i64),
SqlValue::Varchar(arcstr::ArcStr::from(format!("Employee{}", i))),
]),
)
.unwrap();
}
db.create_index(
"idx_dept".to_string(),
"employees".to_string(),
false, vec![IndexColumn::Column {
column_name: "department".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT COUNT(*) FROM employees WHERE department = 'Engineering'";
let stmt = Parser::parse_sql(query).unwrap();
if let vibesql_ast::Statement::Select(select_stmt) = stmt {
let result = executor.execute(&select_stmt).unwrap();
assert_eq!(result.len(), 1, "COUNT should return 1 row");
match &result[0].values[0] {
SqlValue::Integer(count) => {
assert_eq!(
*count, 1675,
"Should return exactly 1,675 Engineering employees, got {}",
count
);
}
_ => panic!("Expected integer count"),
}
} else {
panic!("Expected SELECT statement");
}
let query2 = "SELECT * FROM employees WHERE department = 'Sales' LIMIT 10";
let stmt2 = Parser::parse_sql(query2).unwrap();
if let vibesql_ast::Statement::Select(select_stmt) = stmt2 {
let result = executor.execute(&select_stmt).unwrap();
assert_eq!(result.len(), 10, "Should return 10 Sales employees");
} else {
panic!("Expected SELECT statement");
}
}