diff --git a/datafusion/sql/src/expr/identifier.rs b/datafusion/sql/src/expr/identifier.rs index e9e24b97160e5..371a80516a363 100644 --- a/datafusion/sql/src/expr/identifier.rs +++ b/datafusion/sql/src/expr/identifier.rs @@ -18,12 +18,12 @@ use arrow::datatypes::FieldRef; use datafusion_common::datatype::DataTypeExt; use datafusion_common::{ - Column, DFSchema, Result, Span, TableReference, assert_or_internal_err, + Column, DFSchema, Result, ScalarValue, Span, TableReference, assert_or_internal_err, exec_datafusion_err, internal_err, not_impl_err, plan_datafusion_err, plan_err, }; use datafusion_expr::expr::LambdaVariable; -use datafusion_expr::planner::PlannerResult; -use datafusion_expr::{Case, Expr}; +use datafusion_expr::planner::{PlannerResult, RawFieldAccessExpr}; +use datafusion_expr::{Case, Expr, GetFieldAccess}; use sqlparser::ast::{CaseWhen, Expr as SQLExpr, Ident}; use std::sync::Arc; @@ -144,6 +144,33 @@ impl SqlToRel<'_, S> { })?; Ok(Expr::ScalarVariable(field, var_names)) } else { + let root_name = self.ident_normalizer.normalize(ids[0].clone()); + if planner_context.lambda_parameters().contains_key(&root_name) { + let mut expr = + self.sql_identifier_to_expr(ids[0].clone(), schema, planner_context)?; + 'fields: for id in ids.into_iter().skip(1) { + let field_access = GetFieldAccess::NamedStructField { + name: ScalarValue::from(self.ident_normalizer.normalize(id)), + }; + let mut field_access_expr = RawFieldAccessExpr { expr, field_access }; + for planner in self.context_provider.get_expr_planners() { + match planner.plan_field_access(field_access_expr, schema)? { + PlannerResult::Planned(planned) => { + expr = planned; + continue 'fields; + } + PlannerResult::Original(original) => { + field_access_expr = original; + } + } + } + return not_impl_err!( + "GetFieldAccess not supported by ExprPlanner: {field_access_expr:?}" + ); + } + return Ok(expr); + } + let ids = ids .into_iter() .map(|id| self.ident_normalizer.normalize(id)) diff --git a/datafusion/sqllogictest/test_files/array/array_transform.slt b/datafusion/sqllogictest/test_files/array/array_transform.slt index 5439d7441155b..707c7cd0985aa 100644 --- a/datafusion/sqllogictest/test_files/array/array_transform.slt +++ b/datafusion/sqllogictest/test_files/array/array_transform.slt @@ -133,6 +133,33 @@ SELECT array_transform([[10, 20]], v -> v[1]); ---- [10] +# dotted field access on a struct-valued lambda parameter +query ? +SELECT array_transform( + make_array(named_struct('rule_number', 1, 'rule_action', 'allow')), + e -> e.rule_action +); +---- +[allow] + +# dotted field access followed by a list subscript +query ? +SELECT array_transform( + make_array(named_struct('items', make_array(10, 20))), + e -> e.items[1] +); +---- +[10] + +# chained dotted field access on nested structs +query ? +SELECT array_transform( + make_array(named_struct('metadata', named_struct('rule_action', 'deny'))), + e -> e.metadata.rule_action +); +---- +[deny] + # expr simplifier inside lambda body query TT