use std::fmt;
use datafusion::{
logical_expr::{Expr, LogicalPlan, SortExpr, planner::RelationPlannerContext},
sql::{
planner::NullOrdering,
sqlparser::ast::{
Expr as SqlExpr, OrderByExpr, OrderByKind, OrderByOptions, Query as SqlQuery, Value, ValueWithSpan,
},
},
};
use crate::error::{Error, Result};
pub(crate) fn resolve_input_ordering(
query: &SqlQuery,
planned: &LogicalPlan,
context: &mut dyn RelationPlannerContext,
key_not_projected: impl Fn(String) -> Error,
) -> Result<Vec<SortExpr>> {
let Some(order_by) = &query.order_by else {
return Ok(Vec::new());
};
let resolved = match (&order_by.kind, &order_by.interpolate) {
(OrderByKind::Expressions(keys), None) => {
let null_ordering =
NullOrdering::from(context.context_provider().options().sql_parser.default_null_ordering.as_str());
keys.iter()
.map(|key| resolve_key(key, planned, context, null_ordering).ok_or_else(|| key.to_string()))
.collect::<std::result::Result<Vec<_>, _>>()
},
_ => Err(order_by.to_string()),
};
match resolved {
Ok(ordering) => Ok(ordering),
Err(_) if query.limit_clause.is_some() || query.fetch.is_some() => Ok(Vec::new()),
Err(key) => Err(key_not_projected(key)),
}
}
fn resolve_key(
key: &OrderByExpr,
planned: &LogicalPlan,
context: &mut dyn RelationPlannerContext,
null_ordering: NullOrdering,
) -> Option<SortExpr> {
let OrderByExpr { expr, options: OrderByOptions { asc, nulls_first }, with_fill: None } = key else {
return None;
};
let schema = planned.schema();
let index = match expr {
SqlExpr::Value(ValueWithSpan { value: Value::Number(position, _), .. }) => {
position.parse::<usize>().ok()?.checked_sub(1).filter(|index| *index < schema.fields().len())?
},
expr => match context.sql_expr_to_logical_expr(expr.clone(), schema).ok()? {
Expr::Column(column) => schema.index_of_column(&column).ok()?,
_ => return None,
},
};
let column = Expr::Column(schema.qualified_field(index).into());
let asc = asc.unwrap_or(true);
Some(SortExpr::new(column, asc, nulls_first.unwrap_or_else(|| null_ordering.nulls_first(asc))))
}
pub(crate) fn input_ordering_expressions(input_orderings: &[Vec<SortExpr>]) -> Vec<Expr> {
input_orderings.iter().flatten().map(|sort| sort.expr.clone()).collect()
}
pub(crate) fn input_orderings_with_expressions(
input_orderings: &[Vec<SortExpr>],
exprs: Vec<Expr>,
) -> Option<Vec<Vec<SortExpr>>> {
if exprs.len() != input_orderings.iter().map(Vec::len).sum::<usize>() {
return None;
}
let mut exprs = exprs.into_iter();
Some(
input_orderings
.iter()
.map(|ordering| ordering.iter().zip(exprs.by_ref()).map(|(sort, expr)| sort.with_expr(expr)).collect())
.collect(),
)
}
pub(crate) fn fmt_input_orderings<'a>(
f: &mut fmt::Formatter,
roles: impl Iterator<Item = &'a str>,
input_orderings: &[Vec<SortExpr>],
) -> fmt::Result {
for (role, ordering) in roles.zip(input_orderings) {
if !ordering.is_empty() {
let keys = ordering.iter().map(ToString::to_string).collect::<Vec<_>>().join(", ");
write!(f, ", input_order[{role}]=[{keys}]")?;
}
}
Ok(())
}