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_numeric_types_db() -> Database {
let mut db = Database::new();
db.catalog.set_case_sensitive_identifiers(false);
let schema = TableSchema::new(
"measurements".to_string(),
vec![
ColumnSchema::new("id".to_string(), DataType::Integer, false),
ColumnSchema::new("int_value".to_string(), DataType::Integer, false),
ColumnSchema::new("real_value".to_string(), DataType::Real, false),
ColumnSchema::new("double_value".to_string(), DataType::DoublePrecision, false),
ColumnSchema::new(
"numeric_value".to_string(),
DataType::Numeric { precision: 10, scale: 2 },
false,
),
],
);
db.create_table(schema).unwrap();
db.insert_row(
"measurements",
Row::new(vec![
SqlValue::Integer(1),
SqlValue::Integer(10),
SqlValue::Real(10.5),
SqlValue::Double(10.75),
SqlValue::Numeric(10.9),
]),
)
.unwrap();
db.insert_row(
"measurements",
Row::new(vec![
SqlValue::Integer(2),
SqlValue::Integer(20),
SqlValue::Real(20.5),
SqlValue::Double(20.75),
SqlValue::Numeric(20.9),
]),
)
.unwrap();
db.insert_row(
"measurements",
Row::new(vec![
SqlValue::Integer(3),
SqlValue::Integer(30),
SqlValue::Real(30.5),
SqlValue::Double(30.75),
SqlValue::Numeric(30.9),
]),
)
.unwrap();
db.insert_row(
"measurements",
Row::new(vec![
SqlValue::Integer(4),
SqlValue::Integer(40),
SqlValue::Real(40.5),
SqlValue::Double(40.75),
SqlValue::Numeric(40.9),
]),
)
.unwrap();
db.insert_row(
"measurements",
Row::new(vec![
SqlValue::Integer(5),
SqlValue::Integer(50),
SqlValue::Real(50.5),
SqlValue::Double(50.75),
SqlValue::Numeric(50.9),
]),
)
.unwrap();
db
}
#[test]
fn test_index_range_scan_between_numeric_types() {
let mut db = create_numeric_types_db();
db.create_index(
"idx_int_value".to_string(),
"measurements".to_string(),
false, vec![IndexColumn::Column {
column_name: "int_value".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
db.create_index(
"idx_real_value".to_string(),
"measurements".to_string(),
false,
vec![IndexColumn::Column {
column_name: "real_value".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
db.create_index(
"idx_double_value".to_string(),
"measurements".to_string(),
false,
vec![IndexColumn::Column {
column_name: "double_value".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT id, int_value FROM measurements WHERE int_value BETWEEN 15 AND 35";
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);
assert_eq!(result[0].values[0], SqlValue::Integer(2));
assert_eq!(result[0].values[1], SqlValue::Integer(20));
assert_eq!(result[1].values[0], SqlValue::Integer(3));
assert_eq!(result[1].values[1], SqlValue::Integer(30));
} else {
panic!("Expected SELECT statement");
}
let query = "SELECT id, real_value FROM measurements WHERE real_value BETWEEN 15.0 AND 35.0";
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);
assert_eq!(result[0].values[0], SqlValue::Integer(2));
assert_eq!(result[1].values[0], SqlValue::Integer(3));
} else {
panic!("Expected SELECT statement");
}
let query =
"SELECT id, double_value FROM measurements WHERE double_value BETWEEN 15.0 AND 35.0";
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);
assert_eq!(result[0].values[0], SqlValue::Integer(2));
assert_eq!(result[1].values[0], SqlValue::Integer(3));
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_index_multi_lookup_large_value_set() {
let mut db = Database::new();
db.catalog.set_case_sensitive_identifiers(false);
let schema = TableSchema::new(
"items".to_string(),
vec![
ColumnSchema::new("id".to_string(), DataType::Integer, false),
ColumnSchema::new("value".to_string(), DataType::Integer, false),
],
);
db.create_table(schema).unwrap();
for i in 0..150 {
db.insert_row("items", Row::new(vec![SqlValue::Integer(i), SqlValue::Integer(i * 10)]))
.unwrap();
}
db.create_index(
"idx_items_value".to_string(),
"items".to_string(),
false,
vec![IndexColumn::Column {
column_name: "value".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let mut in_values = Vec::new();
for i in (0..100).step_by(2) {
in_values.push(i * 10);
}
let in_list = in_values.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(",");
let query = format!("SELECT id FROM items WHERE value IN ({}) ORDER BY id", in_list);
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(), 50);
assert_eq!(result[0].values[0], SqlValue::Integer(0));
assert_eq!(result[1].values[0], SqlValue::Integer(2));
assert_eq!(result[2].values[0], SqlValue::Integer(4));
assert_eq!(result[49].values[0], SqlValue::Integer(98));
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_index_commuted_comparisons() {
let mut db = Database::new();
db.catalog.set_case_sensitive_identifiers(false);
let schema = TableSchema::new(
"numbers".to_string(),
vec![
ColumnSchema::new("id".to_string(), DataType::Integer, false),
ColumnSchema::new("value".to_string(), DataType::Integer, false),
],
);
db.create_table(schema).unwrap();
for i in 1..=10 {
db.insert_row("numbers", Row::new(vec![SqlValue::Integer(i), SqlValue::Integer(i * 10)]))
.unwrap();
}
db.create_index(
"idx_numbers_value".to_string(),
"numbers".to_string(),
false,
vec![IndexColumn::Column {
column_name: "value".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query1 = "SELECT id FROM numbers WHERE value > 50 ORDER BY id";
let stmt1 = Parser::parse_sql(query1).unwrap();
let result1 = if let vibesql_ast::Statement::Select(select_stmt) = stmt1 {
executor.execute(&select_stmt).unwrap()
} else {
panic!("Expected SELECT statement");
};
let query2 = "SELECT id FROM numbers WHERE 50 < value ORDER BY id";
let stmt2 = Parser::parse_sql(query2).unwrap();
let result2 = if let vibesql_ast::Statement::Select(select_stmt) = stmt2 {
executor.execute(&select_stmt).unwrap()
} else {
panic!("Expected SELECT statement");
};
assert_eq!(result1.len(), result2.len());
assert_eq!(result1.len(), 5);
for i in 0..result1.len() {
assert_eq!(result1[i].values[0], result2[i].values[0]);
}
assert_eq!(result1[0].values[0], SqlValue::Integer(6));
assert_eq!(result1[4].values[0], SqlValue::Integer(10));
let query3 = "SELECT id FROM numbers WHERE value >= 60 ORDER BY id";
let stmt3 = Parser::parse_sql(query3).unwrap();
let result3 = if let vibesql_ast::Statement::Select(select_stmt) = stmt3 {
executor.execute(&select_stmt).unwrap()
} else {
panic!("Expected SELECT statement");
};
let query4 = "SELECT id FROM numbers WHERE 60 <= value ORDER BY id";
let stmt4 = Parser::parse_sql(query4).unwrap();
let result4 = if let vibesql_ast::Statement::Select(select_stmt) = stmt4 {
executor.execute(&select_stmt).unwrap()
} else {
panic!("Expected SELECT statement");
};
assert_eq!(result3.len(), result4.len());
assert_eq!(result3.len(), 5);
for i in 0..result3.len() {
assert_eq!(result3[i].values[0], result4[i].values[0]);
}
}
#[test]
fn test_index_numeric_type_normalization() {
let mut db = create_numeric_types_db();
db.create_index(
"idx_int".to_string(),
"measurements".to_string(),
false,
vec![IndexColumn::Column {
column_name: "int_value".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
db.create_index(
"idx_real".to_string(),
"measurements".to_string(),
false,
vec![IndexColumn::Column {
column_name: "real_value".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
db.create_index(
"idx_double".to_string(),
"measurements".to_string(),
false,
vec![IndexColumn::Column {
column_name: "double_value".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
db.create_index(
"idx_numeric".to_string(),
"measurements".to_string(),
false,
vec![IndexColumn::Column {
column_name: "numeric_value".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT id, int_value FROM measurements WHERE int_value > 25.0";
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);
assert_eq!(result[0].values[0], SqlValue::Integer(3));
assert_eq!(result[1].values[0], SqlValue::Integer(4));
assert_eq!(result[2].values[0], SqlValue::Integer(5));
} else {
panic!("Expected SELECT statement");
}
let query = "SELECT id, real_value FROM measurements WHERE real_value < 25";
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);
assert_eq!(result[0].values[0], SqlValue::Integer(1));
assert_eq!(result[1].values[0], SqlValue::Integer(2));
} else {
panic!("Expected SELECT statement");
}
let query = "SELECT id FROM measurements WHERE double_value >= 30.75 AND double_value <= 40.75";
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);
assert_eq!(result[0].values[0], SqlValue::Integer(3));
assert_eq!(result[1].values[0], SqlValue::Integer(4));
} else {
panic!("Expected SELECT statement");
}
let query = "SELECT id FROM measurements WHERE numeric_value BETWEEN 20.0 AND 40.0";
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);
assert_eq!(result[0].values[0], SqlValue::Integer(2));
assert_eq!(result[1].values[0], SqlValue::Integer(3));
} else {
panic!("Expected SELECT statement");
}
}