use {
crate::{
recipe::{MetaRecipe, PlannedRecipe, RecipeUtilities},
types::{ColumnInfo, Row},
Result, Value,
},
rayon::prelude::*,
sqlparser::ast::OrderByExpr,
std::cmp::Ordering,
};
pub struct Order(Vec<PlannedOrderItem>);
impl Order {
pub fn new(order_by: Vec<OrderByExpr>, columns: &[ColumnInfo]) -> Result<Self> {
let order_items = order_by
.into_iter()
.map(|order_by_item| PlannedOrderItem::new(order_by_item, columns))
.collect::<Result<Vec<PlannedOrderItem>>>()?;
Ok(Order(order_items))
}
pub fn execute(self, rows: Vec<Row>) -> Result<Vec<Row>> {
if self.0.is_empty() {
return Ok(rows);
}
let (order_terms, order_item_recipes): (Vec<OrderTerm>, Vec<PlannedRecipe>) = self
.0
.into_iter()
.map(|planned_order_item| {
let PlannedOrderItem(order_term, recipe) = planned_order_item;
(order_term, recipe)
})
.unzip();
let order_terms = OrderTerms(order_terms);
let mut order_rows = rows
.into_par_iter()
.map(|row| {
let order_row = order_item_recipes
.clone()
.into_iter()
.map(|recipe| recipe.simplify_by_row(&row)?.confirm())
.collect::<Result<Vec<Value>>>();
order_row.map(|order_row| (row, order_row))
})
.collect::<Result<Vec<(Row, Vec<Value>)>>>()?;
order_rows.par_sort_unstable_by(|(_, order_row_a), (_, order_row_b)| {
order_terms.sort(order_row_a, order_row_b)
});
Ok(order_rows.into_iter().map(|(row, _)| row).collect())
}
}
struct PlannedOrderItem(OrderTerm, PlannedRecipe);
impl PlannedOrderItem {
pub fn new(order_by_item: OrderByExpr, columns: &[ColumnInfo]) -> Result<Self> {
let OrderByExpr {
expr,
asc,
nulls_first,
} = order_by_item;
let recipe = PlannedRecipe::new(MetaRecipe::new(expr)?.simplify_by_basic()?, columns)?;
let is_asc = asc.unwrap_or(true);
let prefer_nulls = nulls_first.unwrap_or(false);
Ok(PlannedOrderItem(
OrderTerm {
is_asc,
prefer_nulls,
},
recipe,
))
}
}
#[derive(Clone)]
struct OrderTerm {
pub is_asc: bool,
pub prefer_nulls: bool,
}
impl OrderTerm {
pub fn sort(&self, order_item_a: &Value, order_item_b: &Value) -> Option<Ordering> {
let order = match (order_item_a, order_item_b) {
(Value::Null, Value::Null) => Ordering::Equal,
(Value::Null, _) | (_, Value::Null) => {
if self.prefer_nulls {
Ordering::Greater
} else {
Ordering::Less
}
}
(other_a, other_b) => other_a.partial_cmp(other_b).unwrap_or(Ordering::Equal),
};
if order == Ordering::Equal {
None
} else if self.is_asc {
Some(order)
} else {
Some(order.reverse())
}
}
}
struct OrderTerms(Vec<OrderTerm>);
impl OrderTerms {
pub fn sort(&self, order_items_a: &[Value], order_items_b: &[Value]) -> Ordering {
order_items_a
.iter()
.zip(order_items_b)
.zip(self.0.clone())
.find_map(|((order_item_a, order_item_b), order_term)| {
order_term.sort(order_item_a, order_item_b)
})
.unwrap_or(Ordering::Equal)
}
}