mod comparison;
mod parallel;
mod position;
mod resolution;
use super::grouping;
use crate::{errors::ExecutorError, evaluator::CombinedExpressionEvaluator};
pub(crate) use resolution::{
extract_order_by_aggregates, extract_window_aggregates, resolve_order_by_alias,
resolve_order_by_for_aggregates, resolve_where_aliases, resolve_where_aliases_with_schema,
select_list_has_aliases,
};
pub(super) type SortKey = (
vibesql_types::SqlValue,
vibesql_ast::OrderDirection,
Option<vibesql_ast::NullsOrder>,
Option<String>,
);
pub(super) type RowWithSortKeys = (vibesql_storage::Row, Option<Vec<SortKey>>);
pub(super) fn apply_order_by(
mut rows: Vec<RowWithSortKeys>,
order_by: &[vibesql_ast::OrderByItem],
evaluator: &CombinedExpressionEvaluator,
select_list: &[vibesql_ast::SelectItem],
) -> Result<Vec<RowWithSortKeys>, ExecutorError> {
let schema = evaluator.schema();
for (row, sort_keys) in &mut rows {
evaluator.clear_cse_cache();
let mut keys = Vec::new();
for (term_index, order_item) in order_by.iter().enumerate() {
let expr_to_eval =
resolve_order_by_alias(&order_item.expr, select_list, term_index, Some(schema))?;
let key_value = evaluator.eval(expr_to_eval.as_ref(), row)?;
let collation = evaluator.get_expression_collation(expr_to_eval.as_ref());
keys.push((key_value, order_item.direction.clone(), order_item.nulls_order, collation));
}
*sort_keys = Some(keys);
}
parallel::sort_rows(&mut rows);
Ok(rows)
}
#[cfg(test)]
mod tests {
use vibesql_storage::Row;
use vibesql_types::SqlValue;
use super::comparison::compare_rows_by_sort_keys;
use super::*;
#[cfg(feature = "parallel")]
use super::super::parallel::ParallelConfig;
#[cfg(feature = "parallel")]
use rayon::slice::ParallelSliceMut;
#[test]
fn test_sort_with_keys_small_dataset() {
let mut rows: Vec<RowWithSortKeys> = vec![
(
Row::from_vec(vec![SqlValue::Integer(3)]),
Some(vec![(SqlValue::Integer(3), vibesql_ast::OrderDirection::Asc, None, None)]),
),
(
Row::from_vec(vec![SqlValue::Integer(1)]),
Some(vec![(SqlValue::Integer(1), vibesql_ast::OrderDirection::Asc, None, None)]),
),
(
Row::from_vec(vec![SqlValue::Integer(2)]),
Some(vec![(SqlValue::Integer(2), vibesql_ast::OrderDirection::Asc, None, None)]),
),
];
let comparison_fn = |(_, keys_a): &RowWithSortKeys, (_, keys_b): &RowWithSortKeys| {
let keys_a = keys_a.as_ref().unwrap();
let keys_b = keys_b.as_ref().unwrap();
compare_rows_by_sort_keys(keys_a, keys_b)
};
rows.sort_by(comparison_fn);
assert_eq!(rows[0].0.values[0], SqlValue::Integer(1));
assert_eq!(rows[1].0.values[0], SqlValue::Integer(2));
assert_eq!(rows[2].0.values[0], SqlValue::Integer(3));
}
#[test]
fn test_sort_with_keys_large_dataset() {
let mut rows: Vec<RowWithSortKeys> = Vec::new();
for i in (0..15000).rev() {
rows.push((
Row::from_vec(vec![SqlValue::Integer(i)]),
Some(vec![(SqlValue::Integer(i), vibesql_ast::OrderDirection::Asc, None, None)]),
));
}
let comparison_fn = |(_, keys_a): &RowWithSortKeys, (_, keys_b): &RowWithSortKeys| {
let keys_a = keys_a.as_ref().unwrap();
let keys_b = keys_b.as_ref().unwrap();
compare_rows_by_sort_keys(keys_a, keys_b)
};
#[cfg(feature = "parallel")]
{
let config = ParallelConfig::global();
if config.should_parallelize_sort(rows.len()) {
rows.par_sort_by(comparison_fn);
} else {
rows.sort_by(comparison_fn);
}
}
#[cfg(not(feature = "parallel"))]
{
rows.sort_by(comparison_fn);
}
assert_eq!(rows[0].0.values[0], SqlValue::Integer(0));
assert_eq!(rows[1].0.values[0], SqlValue::Integer(1));
assert_eq!(rows[2].0.values[0], SqlValue::Integer(2));
assert_eq!(rows[14997].0.values[0], SqlValue::Integer(14997));
assert_eq!(rows[14998].0.values[0], SqlValue::Integer(14998));
assert_eq!(rows[14999].0.values[0], SqlValue::Integer(14999));
}
#[test]
fn test_sort_descending_with_keys() {
let mut rows: Vec<RowWithSortKeys> = vec![
(
Row::from_vec(vec![SqlValue::Integer(1)]),
Some(vec![(SqlValue::Integer(1), vibesql_ast::OrderDirection::Desc, None, None)]),
),
(
Row::from_vec(vec![SqlValue::Integer(3)]),
Some(vec![(SqlValue::Integer(3), vibesql_ast::OrderDirection::Desc, None, None)]),
),
(
Row::from_vec(vec![SqlValue::Integer(2)]),
Some(vec![(SqlValue::Integer(2), vibesql_ast::OrderDirection::Desc, None, None)]),
),
];
let comparison_fn = |(_, keys_a): &RowWithSortKeys, (_, keys_b): &RowWithSortKeys| {
let keys_a = keys_a.as_ref().unwrap();
let keys_b = keys_b.as_ref().unwrap();
compare_rows_by_sort_keys(keys_a, keys_b)
};
rows.sort_by(comparison_fn);
assert_eq!(rows[0].0.values[0], SqlValue::Integer(3));
assert_eq!(rows[1].0.values[0], SqlValue::Integer(2));
assert_eq!(rows[2].0.values[0], SqlValue::Integer(1));
}
#[test]
fn test_sort_with_nulls() {
let mut rows_asc: Vec<RowWithSortKeys> = vec![
(
Row::from_vec(vec![SqlValue::Integer(2)]),
Some(vec![(SqlValue::Integer(2), vibesql_ast::OrderDirection::Asc, None, None)]),
),
(
Row::from_vec(vec![SqlValue::Null]),
Some(vec![(SqlValue::Null, vibesql_ast::OrderDirection::Asc, None, None)]),
),
(
Row::from_vec(vec![SqlValue::Integer(1)]),
Some(vec![(SqlValue::Integer(1), vibesql_ast::OrderDirection::Asc, None, None)]),
),
];
let comparison_fn = |(_, keys_a): &RowWithSortKeys, (_, keys_b): &RowWithSortKeys| {
let keys_a = keys_a.as_ref().unwrap();
let keys_b = keys_b.as_ref().unwrap();
compare_rows_by_sort_keys(keys_a, keys_b)
};
rows_asc.sort_by(&comparison_fn);
assert_eq!(rows_asc[0].0.values[0], SqlValue::Null);
assert_eq!(rows_asc[1].0.values[0], SqlValue::Integer(1));
assert_eq!(rows_asc[2].0.values[0], SqlValue::Integer(2));
}
#[test]
#[cfg(feature = "parallel")]
fn test_parallel_config_threshold() {
let config = ParallelConfig::global();
assert!(!config.should_parallelize_sort(100));
let large_dataset_size = 15000;
let uses_parallel = config.should_parallelize_sort(large_dataset_size);
if config.num_threads > 1 {
assert!(uses_parallel || config.thresholds.sort > large_dataset_size);
}
}
}