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_products_db() -> Database {
let mut db = Database::new();
db.catalog.set_case_sensitive_identifiers(false);
let products_schema = TableSchema::new(
"products".to_string(),
vec![
ColumnSchema::new("id".to_string(), DataType::Integer, false),
ColumnSchema::new(
"name".to_string(),
DataType::Varchar { max_length: Some(100) },
false,
),
ColumnSchema::new("price".to_string(), DataType::Integer, false),
ColumnSchema::new(
"category".to_string(),
DataType::Varchar { max_length: Some(50) },
false,
),
ColumnSchema::new("stock".to_string(), DataType::Integer, false),
],
);
db.create_table(products_schema).unwrap();
db.insert_row(
"products",
Row::new(vec![
SqlValue::Integer(3),
SqlValue::Varchar(arcstr::ArcStr::from("Keyboard")),
SqlValue::Integer(80),
SqlValue::Varchar(arcstr::ArcStr::from("Electronics")),
SqlValue::Integer(50),
]),
)
.unwrap();
db.insert_row(
"products",
Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("Mouse")),
SqlValue::Integer(30),
SqlValue::Varchar(arcstr::ArcStr::from("Electronics")),
SqlValue::Integer(100),
]),
)
.unwrap();
db.insert_row(
"products",
Row::new(vec![
SqlValue::Integer(5),
SqlValue::Varchar(arcstr::ArcStr::from("Monitor")),
SqlValue::Integer(300),
SqlValue::Varchar(arcstr::ArcStr::from("Electronics")),
SqlValue::Integer(20),
]),
)
.unwrap();
db.insert_row(
"products",
Row::new(vec![
SqlValue::Integer(2),
SqlValue::Varchar(arcstr::ArcStr::from("Desk")),
SqlValue::Integer(200),
SqlValue::Varchar(arcstr::ArcStr::from("Furniture")),
SqlValue::Integer(15),
]),
)
.unwrap();
db.insert_row(
"products",
Row::new(vec![
SqlValue::Integer(4),
SqlValue::Varchar(arcstr::ArcStr::from("Chair")),
SqlValue::Integer(150),
SqlValue::Varchar(arcstr::ArcStr::from("Furniture")),
SqlValue::Integer(30),
]),
)
.unwrap();
db
}
fn create_nullable_db() -> Database {
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, true), ],
);
db.create_table(schema).unwrap();
db.insert_row("items", Row::new(vec![SqlValue::Integer(1), SqlValue::Integer(10)])).unwrap();
db.insert_row("items", Row::new(vec![SqlValue::Integer(2), SqlValue::Null])).unwrap();
db.insert_row("items", Row::new(vec![SqlValue::Integer(3), SqlValue::Integer(5)])).unwrap();
db.insert_row("items", Row::new(vec![SqlValue::Integer(4), SqlValue::Null])).unwrap();
db.insert_row("items", Row::new(vec![SqlValue::Integer(5), SqlValue::Integer(15)])).unwrap();
db
}
#[test]
fn test_order_by_single_column_asc_with_index() {
let mut db = create_products_db();
db.create_index(
"idx_products_price".to_string(),
"products".to_string(),
false,
vec![IndexColumn::Column {
column_name: "price".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT name, price FROM products ORDER BY price ASC";
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(), 5);
assert_eq!(result[0].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Mouse")));
assert_eq!(result[1].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Keyboard")));
assert_eq!(result[2].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Chair")));
assert_eq!(result[3].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Desk")));
assert_eq!(result[4].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Monitor")));
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_order_by_single_column_desc_with_index() {
let mut db = create_products_db();
db.create_index(
"idx_products_price".to_string(),
"products".to_string(),
false,
vec![IndexColumn::Column {
column_name: "price".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT name, price FROM products ORDER BY price DESC";
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(), 5);
assert_eq!(result[0].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Monitor")));
assert_eq!(result[1].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Desk")));
assert_eq!(result[2].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Chair")));
assert_eq!(result[3].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Keyboard")));
assert_eq!(result[4].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Mouse")));
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_order_by_multi_column_with_composite_index() {
let mut db = create_products_db();
db.create_index(
"idx_products_category_price".to_string(),
"products".to_string(),
false,
vec![
IndexColumn::Column {
column_name: "category".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
},
IndexColumn::Column {
column_name: "price".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
},
],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT name, category, price FROM products ORDER BY category, price";
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(), 5);
assert_eq!(result[0].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Mouse")));
assert_eq!(result[0].values[1], SqlValue::Varchar(arcstr::ArcStr::from("Electronics")));
assert_eq!(result[1].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Keyboard")));
assert_eq!(result[1].values[1], SqlValue::Varchar(arcstr::ArcStr::from("Electronics")));
assert_eq!(result[2].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Monitor")));
assert_eq!(result[2].values[1], SqlValue::Varchar(arcstr::ArcStr::from("Electronics")));
assert_eq!(result[3].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Chair")));
assert_eq!(result[3].values[1], SqlValue::Varchar(arcstr::ArcStr::from("Furniture")));
assert_eq!(result[4].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Desk")));
assert_eq!(result[4].values[1], SqlValue::Varchar(arcstr::ArcStr::from("Furniture")));
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_order_by_with_where_clause() {
let mut db = create_products_db();
db.create_index(
"idx_products_price".to_string(),
"products".to_string(),
false,
vec![IndexColumn::Column {
column_name: "price".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT name, price FROM products WHERE category = 'Electronics' ORDER BY price";
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::Varchar(arcstr::ArcStr::from("Mouse")));
assert_eq!(result[1].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Keyboard")));
assert_eq!(result[2].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Monitor")));
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_order_by_partial_index_match_falls_back_to_sort() {
let mut db = create_products_db();
db.create_index(
"idx_products_category_price".to_string(),
"products".to_string(),
false,
vec![
IndexColumn::Column {
column_name: "category".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
},
IndexColumn::Column {
column_name: "price".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
},
],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT name, price, category FROM products ORDER BY price, category";
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(), 5);
assert_eq!(result[0].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Mouse")));
assert_eq!(result[4].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Monitor")));
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_order_by_without_index_uses_sort() {
let db = create_products_db();
let executor = SelectExecutor::new(&db);
let query = "SELECT name, stock FROM products ORDER BY stock";
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(), 5);
let stocks: Vec<i64> = result
.iter()
.map(|row| match &row.values[1] {
SqlValue::Integer(s) => *s,
_ => panic!("Expected integer stock"),
})
.collect();
assert_eq!(stocks, vec![15, 20, 30, 50, 100]);
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_order_by_primary_key_implicit_index() {
let db = create_products_db();
let executor = SelectExecutor::new(&db);
let query = "SELECT id, name FROM products ORDER BY 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(), 5);
let ids: Vec<i64> = result
.iter()
.map(|row| match &row.values[0] {
SqlValue::Integer(id) => *id,
_ => panic!("Expected integer id"),
})
.collect();
assert_eq!(ids, vec![1, 2, 3, 4, 5]);
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_order_by_mixed_asc_desc_no_index() {
let mut db = create_products_db();
db.create_index(
"idx_products_category_price".to_string(),
"products".to_string(),
false,
vec![
IndexColumn::Column {
column_name: "category".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
},
IndexColumn::Column {
column_name: "price".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
},
],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT name, category, price FROM products ORDER BY category ASC, price DESC";
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(), 5);
assert_eq!(result[0].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Monitor")));
assert_eq!(result[1].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Keyboard")));
assert_eq!(result[2].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Mouse")));
assert_eq!(result[3].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Desk")));
assert_eq!(result[4].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Chair")));
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_order_by_positional_reference() {
let mut db = create_products_db();
db.create_index(
"idx_products_price".to_string(),
"products".to_string(),
false,
vec![IndexColumn::Column {
column_name: "price".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT name, price FROM products ORDER BY 2";
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(), 5);
assert_eq!(result[0].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Mouse")));
assert_eq!(result[4].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Monitor")));
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_order_by_alias() {
let mut db = create_products_db();
db.create_index(
"idx_products_price".to_string(),
"products".to_string(),
false,
vec![IndexColumn::Column {
column_name: "price".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT name, price AS cost FROM products ORDER BY cost";
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(), 5);
assert_eq!(result[0].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Mouse")));
assert_eq!(result[4].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Monitor")));
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_order_by_with_nulls_asc() {
let mut db = create_nullable_db();
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 query = "SELECT id, value FROM items ORDER BY value ASC";
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(), 5);
let values: Vec<Option<i64>> = result
.iter()
.map(|row| match &row.values[1] {
SqlValue::Integer(v) => Some(*v),
SqlValue::Null => None,
_ => panic!("Expected integer or null"),
})
.collect();
assert_eq!(values[0], None); assert_eq!(values[1], None); assert_eq!(values[2], Some(5));
assert_eq!(values[3], Some(10));
assert_eq!(values[4], Some(15));
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_order_by_with_nulls_desc() {
let mut db = create_nullable_db();
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 query = "SELECT id, value FROM items ORDER BY value DESC";
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(), 5);
let values: Vec<Option<i64>> = result
.iter()
.map(|row| match &row.values[1] {
SqlValue::Integer(v) => Some(*v),
SqlValue::Null => None,
_ => panic!("Expected integer or null"),
})
.collect();
assert_eq!(values[0], Some(15));
assert_eq!(values[1], Some(10));
assert_eq!(values[2], Some(5));
assert_eq!(values[3], None); assert_eq!(values[4], None); } else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_order_by_multi_column_desc_with_index_reversal() {
let mut db = create_products_db();
db.create_index(
"idx_products_category_price".to_string(),
"products".to_string(),
false,
vec![
IndexColumn::Column {
column_name: "category".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
},
IndexColumn::Column {
column_name: "price".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
},
],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT name, category, price FROM products ORDER BY category DESC, price DESC";
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(), 5);
assert_eq!(result[0].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Desk")));
assert_eq!(result[0].values[1], SqlValue::Varchar(arcstr::ArcStr::from("Furniture")));
assert_eq!(result[1].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Chair")));
assert_eq!(result[1].values[1], SqlValue::Varchar(arcstr::ArcStr::from("Furniture")));
assert_eq!(result[2].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Monitor")));
assert_eq!(result[2].values[1], SqlValue::Varchar(arcstr::ArcStr::from("Electronics")));
assert_eq!(result[3].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Keyboard")));
assert_eq!(result[3].values[1], SqlValue::Varchar(arcstr::ArcStr::from("Electronics")));
assert_eq!(result[4].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Mouse")));
assert_eq!(result[4].values[1], SqlValue::Varchar(arcstr::ArcStr::from("Electronics")));
} else {
panic!("Expected SELECT statement");
}
}
#[test]
fn test_order_by_limit_with_index() {
let mut db = create_products_db();
db.create_index(
"idx_products_price".to_string(),
"products".to_string(),
false,
vec![IndexColumn::Column {
column_name: "price".to_string(),
prefix_length: None,
direction: OrderDirection::Asc,
}],
)
.unwrap();
let executor = SelectExecutor::new(&db);
let query = "SELECT name, price FROM products ORDER BY price LIMIT 3";
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::Varchar(arcstr::ArcStr::from("Mouse")));
assert_eq!(result[1].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Keyboard")));
assert_eq!(result[2].values[0], SqlValue::Varchar(arcstr::ArcStr::from("Chair")));
} else {
panic!("Expected SELECT statement");
}
}