您好,欢迎来到汇意旅游网。
搜索
您的当前位置:首页SparkSQL Analyzed实例源码解析

SparkSQL Analyzed实例源码解析

来源:汇意旅游网

Unresolved LogicalPlan

SparkSQL: select name from student where age > 18;

UnresolvedRelation

UnresolvedRelation(LogicalPlan)叶节点表示未解析的relation(表),包含参数tableIdentifier: TableIdentifier。

Filter

Filter(LogicalPlan)节点表示过滤节点,参数condition为过滤表达式Expression,child为子LogicalPlan(本例中即UnresolvedRelation)。

Filter中condition为GreaterThan expression表达式,其接收left和right两个expression作为自己的children expression,比较两个子expression的计算结果,作为自己的计算结果。

Project

Project(LogicalPlan)节点,包含projectList参数表示需要选择的列(SQL中Select后面跟的列),这里就是Seq(UnresolvecdAttribute(Seq(“NAME“)),child表示子LogicalPlan,这里就是Filter节点。

ResolveRelations

在Analyzed中会调用execute()方法,将此rule应用到逻辑算子树中。execute()中会调用rule(plan),这表示调用object ResolveRelations.apply(plan: LogicalPlan)方法。

resolveOperatorsUp(LogicalPlan继承至TreeNode)

apply方法中调用LogicalPlan 的resolveOperatorsUp方法并将后面的偏函数rule作为参数。自下而上的应用于逻辑算子树上的每一个LogicalPlan节点。

其中mapChildren调用的是继承自TreeNode的方法,会将偏函数rule应用到每一个子节点之上。

rule.applyOrElse(self, identity[LogicalPlan])将偏函数应用到LogicalPlan。偏函数会对传入的参数进行模式匹配,只有匹配成功的参数才会进行处理。在rule偏函数中可以看出,如果LogicalPlan是UnresolvedRelation类型,则调用resolveRelation(u)方法。

最后将应用过偏函数rule的LogicalPlan节点返回。

resolveRelation:具体的解析规则(ResolveRelations)


resolveRelation函数中会对传入的LogicalPlan再次进行模式匹配。如果是UnresolvedRelation,会调用lookupTableFromCatalog(u, defaultDatabase)返回foundRelation。

lookupTableFromCatalog(ResolveRelations)


lookupTableFromCatalog调用Analyzed中的SessionCatalog对象catalog.lookupRelation(tableIdentWithDb)。

lookupRelation(SessionCatalog)


lookupRelation会在globalTempViewManager和externalCatalog查找对应的表,将结果包装在View节点中,View节点再包装在SubqueryAlias节点中,这两个节点均为LogicalPlan的子类型。

最后将SubqueryAlias节点返回。

总结

由于rule.applyOrElse(self, identity[LogicalPlan]),rule是偏函数,只会应用到符合条件的LogicalPlan,所以ResolveRelations只会应用到UnresolvedRlation节点。结果如下:

ResolveRelations解析过程为:

ResolveReferences

Analyzed同样会调用其apply方法,将偏函数rule传入LogicalPlan的resolveOperatorsUp函数,对逻辑算子树自下而上的应用。




这个偏函数rule的匹配很长,我们只关注第一个case和最后一个case。

childrenResolved(LogicalPlan)

对于第一个case匹配情况:

如果LogicalPlan.childrenResolved为false,则放弃对该LogicalPlan应用rule,直接返回本身。

在LogicalPlan中children是继承至TreeNode的函数,返回其所有的LogicalPlan子节点。childrenResolved即为判断所有的子节点是否已经resolved。

LogcailPlan.resolved


当LogicalPlan中所有的expression处于resloved状态以及其子节点也处于resloved状态时,该节点resloved才为true。

Expression.resolved


在Expression中,children也是继承至TreeNode的函数,返回其所有的Expression子节点。当所有的expression子节点为resolved状态(如果子节点为Nill,则children.forall(_.resolved)返回true),且输入类型检查通过时(默认通过),该expression的resloved为True。所以对于一些UnreolvedExpression,其resolved状态为false。

如果Expression没有重写resolved,如果有子节点,则由子节点的resloved状态决定,如果没有子节点,则默认为true。

mapExpressions(LogicalPlan继承至QueryPlan)

对于最后一个case情况:

调用LogicalPlan的mapExpressions函数,并将resolve偏函数传入作为解析规则。先看mapExpressions函数,其继承至QueryPlan,其将resolve解析规则应用到LogicalPlan的所有expression。

transformExpression函数表示调用传入的偏函数reslove应用到expression中,生成新的expression。

recursiveTransform函数表示对传入的参数中的所有元素循环应用(应对Seq类型)transformExpression函数。

mapProductIterator(LogicalPlan继承至TreeNode)

mapProductIterator函数将recursiveTransform传入作为参数。mapProductIterator是继承自TreeNode的方法,对每一个productElement调用recursiveTransform函数。

productElement(LogicalPlan继承至Product)

productElement继承自Product接口,对于case类的函数,其表示构造函数中的每个参数。所以recursiveTransform会应用到具体调用mapexpression的LogicalPlan子类的构造函数的各个参数中,最后返回应用过解析规则的新的构造参数。比如对于Fliter节点:

recursiveTransform会应用到condition这个expression中,child直接返回本身。

makeCopy(LogicalPlan继承至TreeNode)


mapProductIterator将recursiveTransform函数应用到所有的构造参数(LogicalPlan的expression是通过构造函数传进来的)中,返回解析之后的构造参数,利用新的构造参数,创建新的LogicalPlan返回。

reslove:具体的解析规则(ResolveReferences)

如果处理的expression是UnresolvedAttribute(nameParts),则会调用LogicalPlan的resolveChildren函数进行解析。nameParts: Seq[String]是未解析属性的字符串。Resolver是一个字符串比较器,其定义于SQLConf中,由下可以看出,resolver就是用来比较字符串的。



如果处理的expression是其他类型不能直接进行解析,会调用mapChildren函数,将resolve偏函数传入,遍历expression的子节点,对其应用resolve进行解析。

resolveChildren(LogicalPlan)

LogicalPlan的resolveChildren函数,利用所有子节点的ouput属性(output表示LogicalPlan输出的Attributes),将传入的未解析的属性字符串解析为NamedExpression(实际类型为AttributeReference)。

output(LogicalPlan继承自QueryPlan)

output表示LogicalPlan输出的Attributes。例如Filter节点的子节点为SubqueryAlias,其重写了output方法:

其会调用child的output方法,即View节点的output。

case Class利用构造函数中的ouput参数重写了output方法,返回传入的Seq[Attribute],由第一步我们可知View节点中的output是由catalog中获取到的表的元数据转换来的Attributes。

在SubqueryAlias中,会对View的output调用withQualifier方法,传入的参数是别名。这个withQualifier没太认真看,但是应该是根据别名,返回AttributeReference。

childAttributes.resolve (LogicalPlan)

上一小节只是说明了childAttributes是由其LoicalPlan的子节点的output输出Attributes组成的Seq。其中Attribute为AttributeReference类型。

LoicalPlan的resolveChildren函数会利用childAttributes对传入的未解析的属性的字符串解析成NamedExpression。这个方法就不展开分析了,LogicalPlan中的UnReslovedAttribute(nameParts:Seq[String])必定使用的是其子节点output的属性。

所以利用childAttributes可将UnresolvedAttribute进行解析。

mapChildren(LogicalPlan继承至TreeNode)

上面一小节只讨论为了ResolveReferences中的resolve方法的case UnresolvedAttribute(nameParts)。对于LogicalPlan中的UnresolvedAttribute你可以直接对其进行解析,但是有些LogicalPlan中并不直接包含UnresolvedAttribute expression,比如Filter中包含GreaterThan expression,但是其包含类型为UnresolvedAttribute的expression子节点。必须要对这种expression进行遍历处理。

第四个case,调用Expression的mapChildren方法,将resovle函数传入,注意将当前LogicalPlan作为参数传入了resovle函数,因为resolve函数需要使用当前LogicalPlan的childAttributes。

mapChildren继承至TreeNode函数,会将resolve函数应用于每一个子节点。

children(LogicalPlan继承自TreeNode)

children是定义在TreeNode中的虚函数。由于Expression和QueryPlan(LogicalPlan和SparkPlan是QueryPlan的子节点)都是其子节点,所以Expression和LogicalPlan中均含有children函数。所以Expression和QueryPlan调用继承至mapChildren时,其遍历的都是定义在自身内部的children函数。

对于Expression,其分为三大类LeafExpression、UnaryExpression、BinaryExpression分别表示含有0、1、2个子节点。其内部都重新定义了children函数。由下可以看出children函数都返回一个Seq,其包含的都是内部定义的虚函数。这些函数需要具体子类去实现。



在具体实现的子类当中,其子类的构造函数中会包含children的内部成员。

GreaterThan(left: Expression, right: Expression)是继承自BinaryExpression的类,其用case修饰,所以构造函数传入的left: Expression, right:
Expression自动重写了BinaryExpression类中的 def left: Expression和def right: Expression虚函数,所以GreaterThan expression调用children函数返回的是其构造函数中传入的left和right子节点。

对于LogicalPlan体系,chidren函数的调用是类似的。

总结

关于为什么Project节点中的name没有被解析,这是因为Filter节点中的18还未被规则解析为字面量,所以Filter节点的resolved状态还为false,所以不能对Project节点进行解析,需要等待下一轮解析。

ResolveReference解析如下:

  1. Analyzed将ResolveReference应用到Unresolved LogicalPlan算子树的根节点之上。根节点调用resolveOperatorsUp对逻辑算子树自下而上的应用偏函数rule。最后返回应用过偏函数rule的根节点。
  2. 偏函数rule会对传入的LogicalPlan进行模式匹配,如果LogicalPlan.childrenResolved为false,则说明子节点还未resolved,则跳过解析,直接返回本身。否则调用LogicalPlan的mapExpressions,对LogicalPlan的所有expression应用偏函数reslove进行解析。reslove返回解析过的Expreesion。mapExpressions返回解析后的LogicalPlan。
  3. 偏函数reslove对传入的Expression进行模式匹配。如果是UnresolvedAttribute,则调用LogicalPlan的resolveChildren函数进行解析。resolveChildren函数会利用childAttributes对UnresolvedAttribute进行解析,获得AttributeReference。
  4. 偏函数reslove对传入的Expression进行模式匹配。如果是其他类型的Expression,则会调用Expression的mapChildren方法,将resovle偏函数作为参数传入。mapChildren会遍历Expression的所有子expression节点,对其应用resovle偏函数,最后得到所有子节点都解析过的Expression。

参考:《Spark SQL内核剖析》

以上个人理解,如果有误,请指正!

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- hids.cn 版权所有 赣ICP备2024042780号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务