use std::sync::Arc;
use arrow::datatypes::DataType;
use datafusion::{logical_expr::Operator, physical_expr::PhysicalExpr, physical_expr_common::physical_expr::fmt_sql};
const MAX_RENDERED_SQL_CHARS: usize = 160;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ExpressionCapability {
PhysicalExpression,
NegativeExpression,
DynamicFilter,
DataFusionTypeResolution,
Literal,
CompatibleTypes,
CastPair,
TryCastPair,
BinaryOperator,
BinaryOperandTypes,
IntervalArithmetic,
LikeCaseInsensitive,
LikePattern,
InList,
ScalarFunction,
ScalarFunctionArguments,
SubstringArguments,
ExtractField,
JoinResidualShape,
JoinResidualSides,
JoinResidualSideSchema,
JoinResidualCastTarget,
JoinResidualScalarFunction,
CaseActivationSize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ExpressionRejectionKind {
UnsupportedExpression,
UnsupportedType,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum ExpressionRejectionSubject {
Unnamed,
ScalarFunction(String),
Cast {
source: DataType,
target: DataType,
},
LiteralType(DataType),
DynamicFilterError(String),
BinaryOperator {
operator: Operator,
left: DataType,
right: DataType,
},
UnaryOperator {
operator: &'static str,
operand: DataType,
},
ValueType(DataType),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ExpressionRejection {
kind: ExpressionRejectionKind,
capability: ExpressionCapability,
subject: ExpressionRejectionSubject,
sql: Option<String>,
}
impl ExpressionRejection {
pub(super) const fn unsupported_expression(capability: ExpressionCapability) -> Self {
Self {
kind: ExpressionRejectionKind::UnsupportedExpression,
capability,
subject: ExpressionRejectionSubject::Unnamed,
sql: None,
}
}
pub(super) const fn unsupported_type(capability: ExpressionCapability) -> Self {
Self {
kind: ExpressionRejectionKind::UnsupportedType,
capability,
subject: ExpressionRejectionSubject::Unnamed,
sql: None,
}
}
pub(in crate::native) fn unsupported_value_type(expr: &Arc<dyn PhysicalExpr>, data_type: DataType) -> Self {
Self::unsupported_type(ExpressionCapability::PhysicalExpression)
.with_subject(ExpressionRejectionSubject::ValueType(data_type))
.at_expression(expr)
}
pub(super) fn with_subject(mut self, subject: ExpressionRejectionSubject) -> Self {
self.subject = subject;
self
}
pub(super) fn named_by_operator(self, subject: Option<ExpressionRejectionSubject>) -> Self {
match subject {
Some(subject) if self.sql.is_none() && self.subject == ExpressionRejectionSubject::Unnamed => {
self.with_subject(subject)
},
_ => self,
}
}
pub(super) fn at_expression(mut self, expr: &Arc<dyn PhysicalExpr>) -> Self {
if self.sql.is_none() {
self.sql = Some(render_sql(expr.as_ref()));
}
self
}
pub(super) fn for_scalar_function(self, name: &str) -> Self {
if self.capability == ExpressionCapability::ScalarFunctionArguments
&& self.subject == ExpressionRejectionSubject::Unnamed
{
self.with_subject(ExpressionRejectionSubject::ScalarFunction(name.to_string()))
} else {
self
}
}
pub(crate) const fn kind(&self) -> ExpressionRejectionKind {
self.kind
}
pub(crate) const fn capability(&self) -> ExpressionCapability {
self.capability
}
pub(crate) const fn subject(&self) -> &ExpressionRejectionSubject {
&self.subject
}
pub(crate) fn sql(&self) -> Option<&str> {
self.sql.as_deref()
}
}
fn render_sql(expr: &dyn PhysicalExpr) -> String {
let sql = fmt_sql(expr).to_string();
match sql.char_indices().nth(MAX_RENDERED_SQL_CHARS) {
Some((end, _)) => format!("{}...", &sql[..end]),
None => sql,
}
}