@@ -72,32 +72,45 @@ abstract class LogicalPlan extends QueryPlan[LogicalPlan] {
7272 def childrenResolved : Boolean = ! children.exists(! _.resolved)
7373
7474 /**
75- * Optionally resolves the given string to a [[NamedExpression ]]. The attribute is expressed as
75+ * Optionally resolves the given string to a [[NamedExpression ]] using the input from all child
76+ * nodes of this LogicalPlan. The attribute is expressed as
7677 * as string in the following form: `[scope].AttributeName.[nested].[fields]...`.
7778 */
78- def resolve (name : String ): Option [NamedExpression ] = {
79+ def resolveChildren (name : String ): Option [NamedExpression ] =
80+ resolve(name, children.flatMap(_.output))
81+
82+ /**
83+ * Optionally resolves the given string to a [[NamedExpression ]] based on the output of this
84+ * LogicalPlan. The attribute is expressed as string in the following form:
85+ * `[scope].AttributeName.[nested].[fields]...`.
86+ */
87+ def resolve (name : String ): Option [NamedExpression ] =
88+ resolve(name, output)
89+
90+ /** Performs attribute resolution given a name and a sequence of possible attributes. */
91+ protected def resolve (name : String , input : Seq [Attribute ]): Option [NamedExpression ] = {
7992 val parts = name.split(" \\ ." )
8093 // Collect all attributes that are output by this nodes children where either the first part
8194 // matches the name or where the first part matches the scope and the second part matches the
8295 // name. Return these matches along with any remaining parts, which represent dotted access to
8396 // struct fields.
84- val options = children.flatMap(_.output) .flatMap { option =>
97+ val options = input .flatMap { option =>
8598 // If the first part of the desired name matches a qualifier for this possible match, drop it.
8699 val remainingParts =
87100 if (option.qualifiers.contains(parts.head) && parts.size > 1 ) parts.drop(1 ) else parts
88101 if (option.name == remainingParts.head) (option, remainingParts.tail.toList) :: Nil else Nil
89102 }
90103
91104 options.distinct match {
92- case ( a, Nil ) :: Nil => Some (a) // One match, no nested fields, use it.
105+ case Seq (( a, Nil )) => Some (a) // One match, no nested fields, use it.
93106 // One match, but we also need to extract the requested nested field.
94- case ( a, nestedFields) :: Nil =>
107+ case Seq (( a, nestedFields)) =>
95108 a.dataType match {
96109 case StructType (fields) =>
97110 Some (Alias (nestedFields.foldLeft(a : Expression )(GetField ), nestedFields.last)())
98111 case _ => None // Don't know how to resolve these field references
99112 }
100- case Nil => None // No matches.
113+ case Seq () => None // No matches.
101114 case ambiguousReferences =>
102115 throw new TreeNodeException (
103116 this , s " Ambiguous references to $name: ${ambiguousReferences.mkString(" ," )}" )
0 commit comments