blob: 79ebc5943ffbe0da8413dc5145c979f1dba81ba0 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use crate::planner::{ContextProvider, PlannerContext, SqlToRel};
use datafusion_common::{
not_impl_err, plan_datafusion_err, plan_err, Column, DFSchema, Result,
};
use datafusion_expr::expr::Sort;
use datafusion_expr::{Expr, SortExpr};
use sqlparser::ast::{
Expr as SQLExpr, OrderByExpr, OrderByOptions, Value, ValueWithSpan,
};
impl<S: ContextProvider> SqlToRel<'_, S> {
/// Convert sql [OrderByExpr] to `Vec<Expr>`.
///
/// `input_schema` and `additional_schema` are used to resolve column references in the order-by expressions.
/// `input_schema` is the schema of the input logical plan, typically derived from the SELECT list.
///
/// Usually order-by expressions can only reference the input plan's columns.
/// But the `SELECT ... FROM ... ORDER BY ...` syntax is a special case. Besides the input schema,
/// it can reference an `additional_schema` derived from the `FROM` clause.
///
/// If `literal_to_column` is true, treat any numeric literals (e.g. `2`) as a 1 based index into the
/// SELECT list (e.g. `SELECT a, b FROM table ORDER BY 2`). Literals only reference the `input_schema`.
///
/// If false, interpret numeric literals as constant values.
pub(crate) fn order_by_to_sort_expr(
&self,
order_by_exprs: Vec<OrderByExpr>,
input_schema: &DFSchema,
planner_context: &mut PlannerContext,
literal_to_column: bool,
additional_schema: Option<&DFSchema>,
) -> Result<Vec<SortExpr>> {
if order_by_exprs.is_empty() {
return Ok(vec![]);
}
let mut combined_schema;
let order_by_schema = match additional_schema {
Some(schema) => {
combined_schema = input_schema.clone();
combined_schema.merge(schema);
&combined_schema
}
None => input_schema,
};
let mut sort_expr_vec = Vec::with_capacity(order_by_exprs.len());
let make_sort_expr = |expr: Expr,
asc: Option<bool>,
nulls_first: Option<bool>| {
let asc = asc.unwrap_or(true);
let nulls_first = nulls_first
.unwrap_or_else(|| self.options.default_null_ordering.nulls_first(asc));
Sort::new(expr, asc, nulls_first)
};
for order_by_expr in order_by_exprs {
let OrderByExpr {
expr,
options: OrderByOptions { asc, nulls_first },
with_fill,
} = order_by_expr;
if let Some(with_fill) = with_fill {
return not_impl_err!("ORDER BY WITH FILL is not supported: {with_fill}");
}
let expr = match expr {
SQLExpr::Value(ValueWithSpan {
value: Value::Number(v, _),
span: _,
}) if literal_to_column => {
let field_index = v
.parse::<usize>()
.map_err(|err| plan_datafusion_err!("{}", err))?;
if field_index == 0 {
return plan_err!(
"Order by index starts at 1 for column indexes"
);
} else if input_schema.fields().len() < field_index {
return plan_err!(
"Order by column out of bounds, specified: {}, max: {}",
field_index,
input_schema.fields().len()
);
}
Expr::Column(Column::from(
input_schema.qualified_field(field_index - 1),
))
}
e => {
self.sql_expr_to_logical_expr(e, order_by_schema, planner_context)?
}
};
sort_expr_vec.push(make_sort_expr(expr, asc, nulls_first));
}
Ok(sort_expr_vec)
}
}