@@ -21,13 +21,13 @@ use datafusion_expr::planner::{
21
21
} ;
22
22
use sqlparser:: ast:: {
23
23
AccessExpr , BinaryOperator , CastFormat , CastKind , DataType as SQLDataType ,
24
- DictionaryField , Expr as SQLExpr , ExprWithAlias as SQLExprWithAlias , MapEntry ,
25
- StructField , Subscript , TrimWhereField , Value ,
24
+ DictionaryField , Expr as SQLExpr , ExprWithAlias as SQLExprWithAlias , Ident , MapEntry ,
25
+ Spanned , StructField , Subscript , TrimWhereField , Value ,
26
26
} ;
27
27
28
28
use datafusion_common:: {
29
29
internal_datafusion_err, internal_err, not_impl_err, plan_err, Column , DFSchema ,
30
- Result , ScalarValue ,
30
+ Result , ScalarValue , Span ,
31
31
} ;
32
32
33
33
use datafusion_expr:: expr:: ScalarFunction ;
@@ -983,14 +983,100 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
983
983
Ok ( Expr :: Cast ( Cast :: new ( Box :: new ( expr) , dt) ) )
984
984
}
985
985
986
+ /// Extracts the root expression and access chain from a compound expression.
987
+ ///
988
+ /// This function attempts to identify if a compound expression (like `a.b.c`) should be treated
989
+ /// as a column reference with a qualifier (like `table.column`) or as a field access expression.
990
+ ///
991
+ /// # Arguments
992
+ ///
993
+ /// * `root` - The root SQL expression (e.g., the first part of `a.b.c`)
994
+ /// * `access_chain` - Vector of access expressions (e.g., `.b` and `.c` parts)
995
+ /// * `schema` - The schema to resolve column references against
996
+ /// * `planner_context` - Context for planning expressions
997
+ ///
998
+ /// # Returns
999
+ ///
1000
+ /// A tuple containing:
1001
+ /// * The resolved root expression
1002
+ /// * The remaining access chain that should be processed as field accesses
1003
+ fn extract_root_and_access_chain (
1004
+ & self ,
1005
+ root : SQLExpr ,
1006
+ access_chain : Vec < AccessExpr > ,
1007
+ schema : & DFSchema ,
1008
+ planner_context : & mut PlannerContext ,
1009
+ ) -> Result < ( Expr , Vec < AccessExpr > ) > {
1010
+ if let SQLExpr :: Identifier ( Ident { value : id, .. } ) = & root {
1011
+ let mut ids = vec ! [ id. clone( ) ] ;
1012
+ for access in & access_chain {
1013
+ if let AccessExpr :: Dot ( SQLExpr :: Identifier ( Ident { value : id, .. } ) ) =
1014
+ access
1015
+ {
1016
+ ids. push ( id. clone ( ) ) ;
1017
+ } else {
1018
+ break ;
1019
+ }
1020
+ }
1021
+
1022
+ if ids. len ( ) > 1 {
1023
+ // maybe it's a compound identifier
1024
+ if let Some ( ( field, Some ( qualifier) , nested_names) ) =
1025
+ identifier:: search_dfschema ( & ids, schema)
1026
+ {
1027
+ let span_num = ids. len ( ) - nested_names. len ( ) ;
1028
+ let mut idx = 0 ;
1029
+ let mut spans = vec ! [ ] ;
1030
+ if let Some ( s) = Span :: try_from_sqlparser_span ( root. span ( ) ) {
1031
+ spans. push ( s) ;
1032
+ }
1033
+ idx += 1 ;
1034
+
1035
+ let mut nested_access_chain = vec ! [ ] ;
1036
+ for access in & access_chain {
1037
+ if idx < span_num {
1038
+ idx += 1 ;
1039
+ if let AccessExpr :: Dot ( expr) = access {
1040
+ if let Some ( s) =
1041
+ Span :: try_from_sqlparser_span ( expr. span ( ) )
1042
+ {
1043
+ spans. push ( s) ;
1044
+ }
1045
+ } else {
1046
+ unreachable ! ( ) ;
1047
+ }
1048
+ } else {
1049
+ nested_access_chain. push ( access. clone ( ) ) ;
1050
+ }
1051
+ }
1052
+
1053
+ let root = Expr :: Column ( Column {
1054
+ name : field. name ( ) . clone ( ) ,
1055
+ relation : Some ( qualifier. clone ( ) ) ,
1056
+ spans : datafusion_common:: Spans ( spans) ,
1057
+ } ) ;
1058
+ let access_chain = nested_access_chain;
1059
+ return Ok ( ( root, access_chain) ) ;
1060
+ }
1061
+ }
1062
+ }
1063
+ let root = self . sql_expr_to_logical_expr ( root, schema, planner_context) ?;
1064
+ Ok ( ( root, access_chain) )
1065
+ }
1066
+
986
1067
fn sql_compound_field_access_to_expr (
987
1068
& self ,
988
1069
root : SQLExpr ,
989
1070
access_chain : Vec < AccessExpr > ,
990
1071
schema : & DFSchema ,
991
1072
planner_context : & mut PlannerContext ,
992
1073
) -> Result < Expr > {
993
- let mut root = self . sql_expr_to_logical_expr ( root, schema, planner_context) ?;
1074
+ let ( mut root, access_chain) = self . extract_root_and_access_chain (
1075
+ root,
1076
+ access_chain,
1077
+ schema,
1078
+ planner_context,
1079
+ ) ?;
994
1080
let fields = access_chain
995
1081
. into_iter ( )
996
1082
. map ( |field| match field {
0 commit comments