in datafusion/sql/src/expr/mod.rs [201:608]
fn sql_expr_to_logical_expr_internal(
&self,
sql: SQLExpr,
schema: &DFSchema,
planner_context: &mut PlannerContext,
) -> Result<Expr> {
// NOTE: This function is called recursively, so each match arm body should be as
// small as possible to decrease stack requirement.
// Follow the common pattern of extracting into a separate function for
// non-trivial arms. See https://github.com/apache/datafusion/pull/12384 for
// more context.
match sql {
SQLExpr::Value(value) => {
self.parse_value(value.into(), planner_context.prepare_param_data_types())
}
SQLExpr::Extract { field, expr, .. } => {
let mut extract_args = vec![
Expr::Literal(ScalarValue::from(format!("{field}"))),
self.sql_expr_to_logical_expr(*expr, schema, planner_context)?,
];
for planner in self.context_provider.get_expr_planners() {
match planner.plan_extract(extract_args)? {
PlannerResult::Planned(expr) => return Ok(expr),
PlannerResult::Original(args) => {
extract_args = args;
}
}
}
not_impl_err!("Extract not supported by ExprPlanner: {extract_args:?}")
}
SQLExpr::Array(arr) => self.sql_array_literal(arr.elem, schema),
SQLExpr::Interval(interval) => self.sql_interval_to_expr(false, interval),
SQLExpr::Identifier(id) => {
self.sql_identifier_to_expr(id, schema, planner_context)
}
// <expr>["foo"], <expr>[4] or <expr>[4:5]
SQLExpr::CompoundFieldAccess { root, access_chain } => self
.sql_compound_field_access_to_expr(
*root,
access_chain,
schema,
planner_context,
),
SQLExpr::CompoundIdentifier(ids) => {
self.sql_compound_identifier_to_expr(ids, schema, planner_context)
}
SQLExpr::Case {
operand,
conditions,
else_result,
} => self.sql_case_identifier_to_expr(
operand,
conditions,
else_result,
schema,
planner_context,
),
SQLExpr::Cast {
kind: CastKind::Cast | CastKind::DoubleColon,
expr,
data_type,
format,
} => self.sql_cast_to_expr(*expr, data_type, format, schema, planner_context),
SQLExpr::Cast {
kind: CastKind::TryCast | CastKind::SafeCast,
expr,
data_type,
format,
} => {
if let Some(format) = format {
return not_impl_err!("CAST with format is not supported: {format}");
}
Ok(Expr::TryCast(TryCast::new(
Box::new(self.sql_expr_to_logical_expr(
*expr,
schema,
planner_context,
)?),
self.convert_data_type(&data_type)?,
)))
}
SQLExpr::TypedString { data_type, value } => Ok(Expr::Cast(Cast::new(
Box::new(lit(value.into_string().unwrap())),
self.convert_data_type(&data_type)?,
))),
SQLExpr::IsNull(expr) => Ok(Expr::IsNull(Box::new(
self.sql_expr_to_logical_expr(*expr, schema, planner_context)?,
))),
SQLExpr::IsNotNull(expr) => Ok(Expr::IsNotNull(Box::new(
self.sql_expr_to_logical_expr(*expr, schema, planner_context)?,
))),
SQLExpr::IsDistinctFrom(left, right) => {
Ok(Expr::BinaryExpr(BinaryExpr::new(
Box::new(self.sql_expr_to_logical_expr(
*left,
schema,
planner_context,
)?),
Operator::IsDistinctFrom,
Box::new(self.sql_expr_to_logical_expr(
*right,
schema,
planner_context,
)?),
)))
}
SQLExpr::IsNotDistinctFrom(left, right) => {
Ok(Expr::BinaryExpr(BinaryExpr::new(
Box::new(self.sql_expr_to_logical_expr(
*left,
schema,
planner_context,
)?),
Operator::IsNotDistinctFrom,
Box::new(self.sql_expr_to_logical_expr(
*right,
schema,
planner_context,
)?),
)))
}
SQLExpr::IsTrue(expr) => Ok(Expr::IsTrue(Box::new(
self.sql_expr_to_logical_expr(*expr, schema, planner_context)?,
))),
SQLExpr::IsFalse(expr) => Ok(Expr::IsFalse(Box::new(
self.sql_expr_to_logical_expr(*expr, schema, planner_context)?,
))),
SQLExpr::IsNotTrue(expr) => Ok(Expr::IsNotTrue(Box::new(
self.sql_expr_to_logical_expr(*expr, schema, planner_context)?,
))),
SQLExpr::IsNotFalse(expr) => Ok(Expr::IsNotFalse(Box::new(
self.sql_expr_to_logical_expr(*expr, schema, planner_context)?,
))),
SQLExpr::IsUnknown(expr) => Ok(Expr::IsUnknown(Box::new(
self.sql_expr_to_logical_expr(*expr, schema, planner_context)?,
))),
SQLExpr::IsNotUnknown(expr) => Ok(Expr::IsNotUnknown(Box::new(
self.sql_expr_to_logical_expr(*expr, schema, planner_context)?,
))),
SQLExpr::UnaryOp { op, expr } => {
self.parse_sql_unary_op(op, *expr, schema, planner_context)
}
SQLExpr::Between {
expr,
negated,
low,
high,
} => Ok(Expr::Between(Between::new(
Box::new(self.sql_expr_to_logical_expr(
*expr,
schema,
planner_context,
)?),
negated,
Box::new(self.sql_expr_to_logical_expr(*low, schema, planner_context)?),
Box::new(self.sql_expr_to_logical_expr(
*high,
schema,
planner_context,
)?),
))),
SQLExpr::InList {
expr,
list,
negated,
} => self.sql_in_list_to_expr(*expr, list, negated, schema, planner_context),
SQLExpr::Like {
negated,
expr,
pattern,
escape_char,
any,
} => self.sql_like_to_expr(
negated,
*expr,
*pattern,
escape_char,
schema,
planner_context,
false,
any,
),
SQLExpr::ILike {
negated,
expr,
pattern,
escape_char,
any,
} => self.sql_like_to_expr(
negated,
*expr,
*pattern,
escape_char,
schema,
planner_context,
true,
any,
),
SQLExpr::SimilarTo {
negated,
expr,
pattern,
escape_char,
} => self.sql_similarto_to_expr(
negated,
*expr,
*pattern,
escape_char,
schema,
planner_context,
),
SQLExpr::BinaryOp { .. } => {
internal_err!("binary_op should be handled by sql_expr_to_logical_expr.")
}
#[cfg(feature = "unicode_expressions")]
SQLExpr::Substring {
expr,
substring_from,
substring_for,
special: _,
} => self.sql_substring_to_expr(
expr,
substring_from,
substring_for,
schema,
planner_context,
),
#[cfg(not(feature = "unicode_expressions"))]
SQLExpr::Substring { .. } => {
internal_err!(
"statement substring requires compilation with feature flag: unicode_expressions."
)
}
SQLExpr::Trim {
expr,
trim_where,
trim_what,
trim_characters,
} => self.sql_trim_to_expr(
*expr,
trim_where,
trim_what,
trim_characters,
schema,
planner_context,
),
SQLExpr::Function(function) => {
self.sql_function_to_expr(function, schema, planner_context)
}
SQLExpr::Rollup(exprs) => {
self.sql_rollup_to_expr(exprs, schema, planner_context)
}
SQLExpr::Cube(exprs) => self.sql_cube_to_expr(exprs, schema, planner_context),
SQLExpr::GroupingSets(exprs) => {
self.sql_grouping_sets_to_expr(exprs, schema, planner_context)
}
SQLExpr::Floor {
expr,
field: _field,
} => self.sql_fn_name_to_expr(*expr, "floor", schema, planner_context),
SQLExpr::Ceil {
expr,
field: _field,
} => self.sql_fn_name_to_expr(*expr, "ceil", schema, planner_context),
SQLExpr::Overlay {
expr,
overlay_what,
overlay_from,
overlay_for,
} => self.sql_overlay_to_expr(
*expr,
*overlay_what,
*overlay_from,
overlay_for,
schema,
planner_context,
),
SQLExpr::Nested(e) => {
self.sql_expr_to_logical_expr(*e, schema, planner_context)
}
SQLExpr::Exists { subquery, negated } => {
self.parse_exists_subquery(*subquery, negated, schema, planner_context)
}
SQLExpr::InSubquery {
expr,
subquery,
negated,
} => {
self.parse_in_subquery(*expr, *subquery, negated, schema, planner_context)
}
SQLExpr::Subquery(subquery) => {
self.parse_scalar_subquery(*subquery, schema, planner_context)
}
SQLExpr::Struct { values, fields } => {
self.parse_struct(schema, planner_context, values, fields)
}
SQLExpr::Position { expr, r#in } => {
self.sql_position_to_expr(*expr, *r#in, schema, planner_context)
}
SQLExpr::AtTimeZone {
timestamp,
time_zone,
} => Ok(Expr::Cast(Cast::new(
Box::new(self.sql_expr_to_logical_expr_internal(
*timestamp,
schema,
planner_context,
)?),
match *time_zone {
SQLExpr::Value(ValueWithSpan {
value: Value::SingleQuotedString(s),
span: _,
}) => DataType::Timestamp(TimeUnit::Nanosecond, Some(s.into())),
_ => {
return not_impl_err!(
"Unsupported ast node in sqltorel: {time_zone:?}"
)
}
},
))),
SQLExpr::Dictionary(fields) => {
self.try_plan_dictionary_literal(fields, schema, planner_context)
}
SQLExpr::Map(map) => {
self.try_plan_map_literal(map.entries, schema, planner_context)
}
SQLExpr::AnyOp {
left,
compare_op,
right,
// ANY/SOME are equivalent, this field specifies which the user
// specified but it doesn't affect the plan so ignore the field
is_some: _,
} => {
let mut binary_expr = RawBinaryExpr {
op: compare_op,
left: self.sql_expr_to_logical_expr(
*left,
schema,
planner_context,
)?,
right: self.sql_expr_to_logical_expr(
*right,
schema,
planner_context,
)?,
};
for planner in self.context_provider.get_expr_planners() {
match planner.plan_any(binary_expr)? {
PlannerResult::Planned(expr) => {
return Ok(expr);
}
PlannerResult::Original(expr) => {
binary_expr = expr;
}
}
}
not_impl_err!("AnyOp not supported by ExprPlanner: {binary_expr:?}")
}
#[expect(deprecated)]
SQLExpr::Wildcard(_token) => Ok(Expr::Wildcard {
qualifier: None,
options: Box::new(WildcardOptions::default()),
}),
#[expect(deprecated)]
SQLExpr::QualifiedWildcard(object_name, _token) => Ok(Expr::Wildcard {
qualifier: Some(self.object_name_to_table_reference(object_name)?),
options: Box::new(WildcardOptions::default()),
}),
SQLExpr::Tuple(values) => self.parse_tuple(schema, planner_context, values),
_ => not_impl_err!("Unsupported ast node in sqltorel: {sql:?}"),
}
}