文章目录

  • Unresolved LogicalPlan
    • UnresolvedRelation
    • Filter
    • Project
  • ResolveRelations
    • resolveOperatorsUp(LogicalPlan继承至TreeNode)
    • resolveRelation:具体的解析规则(ResolveRelations)
    • lookupTableFromCatalog(ResolveRelations)
    • lookupRelation(SessionCatalog)
    • 总结
  • ResolveReferences
    • childrenResolved(LogicalPlan)
      • LogcailPlan.resolved
      • Expression.resolved
    • mapExpressions(LogicalPlan继承至QueryPlan)
      • mapProductIterator(LogicalPlan继承至TreeNode)
      • productElement(LogicalPlan继承至Product)
      • makeCopy(LogicalPlan继承至TreeNode)
    • reslove:具体的解析规则(ResolveReferences)
      • resolveChildren(LogicalPlan)
        • output(LogicalPlan继承自QueryPlan)
        • childAttributes.resolve (LogicalPlan)
      • mapChildren(LogicalPlan继承至TreeNode)
        • children(LogicalPlan继承自TreeNode)
    • 总结

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的计算结果,作为自己的计算结果。

left为UnresolvedAttribute(Seq(‘AGE’))表示还未被解析的属性,right为Literal(18, Integetype),表示字面量。

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解析过程为:

  1. Analyzed将ResolveRelations应用到Unresolved LogicalPlan算子树的根节点之上。根节点调用resolveOperatorsUp对逻辑算子树自下而上的应用偏函数rule。最后返回应用过偏函数rule的根节点。
  2. 偏函数rule首先对传入的LogicalPlan进行模式匹配,如果是UnresolvedRelation类型,则调用resolveRelation方法,将当前LogicalPlan作为参数传入,得到解析后的LogicalPlan。否则直接返回本身。
  3. resolveRelation方法调用lookupTableFromCatalog方法。利用Analyzed中的SessionCatalog对象解析表信息,调用catalog的lookupRelation方法,返回解析过的LogicalPlan。
  4. lookupRelation方法从globalTempViewManager或者externalCatalog 查找表信息,并将结果包装在View节点中,再将View节点包装再SubqueryAlias节点中,返回SubqueryAlias节点。

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内核剖析》

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

SparkSQL Analyzed实例源码解析相关推荐

  1. aop实现原理 - JDK动态代理(实例+源码解析)

    动态代理: jdk代理-基于接口代理 通过 类:java.lang.reflect.Proxy 生成动态代理类 实现 接口:InvocationHandler 只能基于接口进行动态代理 代码实现: 1 ...

  2. (Nacos源码解析五)Nacos服务事件变动源码解析

    Nacos源码解析系列目录 Nacos 源码编译运行 (Nacos源码解析一)Nacos 注册实例源码解析 (Nacos源码解析二)Nacos 服务发现源码解析 (Nacos源码解析三)Nacos 心 ...

  3. 注册中心 Eureka 源码解析 —— 应用实例注册发现(五)之过期

    2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-evict/ ...

  4. Guava RateLimiter限流源码解析和实例应用

    2019独角兽企业重金招聘Python工程师标准>>> 在开发高并发系统时有三把利器用来保护系统:缓存.降级和限流 缓存 缓存的目的是提升系统访问速度和增大系统处理容量 降级 降级是 ...

  5. sparksql insertinto 源码解析

    本篇源码解析主要来自于对overwrite覆盖写模式的好奇,想追踪下具体覆盖写的流程和如何进行的覆盖重写? sparksql insertinto 主要功能是向已有表中插入数据,其有四种模式:     ...

  6. 实例源码_SpringBoot数据库源码解析Template实例化操作

    Jdbc TemplateAutoConfiguration 在实践过程中,除了数据源的配置外,我们还会经常用到 Jdbc Template.Jdbc Template是 Spring 对数据库的操作 ...

  7. 谷歌BERT预训练源码解析(一):训练数据生成

    目录 预训练源码结构简介 输入输出 源码解析 参数 主函数 创建训练实例 下一句预测&实例生成 随机遮蔽 输出 结果一览 预训练源码结构简介 关于BERT,简单来说,它是一个基于Transfo ...

  8. Java集合---LinkedList源码解析

    一.源码解析 1. LinkedList类定义 2.LinkedList数据结构原理 3.私有属性 4.构造方法 5.元素添加add()及原理 6.删除数据remove() 7.数据获取get() 8 ...

  9. java treeset原理_Java集合 --- TreeSet底层实现和原理(源码解析)

    概述 文章的内容基于JDK1.7进行分析,之所以选用这个版本,是因为1.8的有些类做了改动,增加了阅读的难度,虽然是1.7,但是对于1.8做了重大改动的内容,文章也会进行说明. TreeSet实现了S ...

  10. Framework 源码解析知识梳理(5) startService 源码分析

    一.前言 最近在看关于插件化的知识,遇到了如何实现Service插件化的问题,因此,先学习一下Service内部的实现原理,这里面会涉及到应用进程和ActivityManagerService的通信, ...

最新文章

  1. LIST 和 MAP
  2. plsql programming 10 日期和时间戳
  3. mysql providername,c#访问各数据库的providerName各驱动
  4. 微信小程序 App()方法与getApp()方法
  5. qt 设置串口起始位_【IT专家】Qt:如何设置主窗口的初始位置?
  6. ACDSee Photo Manager 12 中文绿色版
  7. 搜狗浏览器下 禁止浏览器自动填写用户名、密码
  8. oracle清空数据库命令行,使用命令行手动卸载Oracle Database 11gR2
  9. 高并发大容量 NoSQL 解决方案探索
  10. 亿欧:深耕开放银行,Temenos产品创新和并购战略双轮驱动
  11. Ubuntu 离线安装软件包
  12. 微软sql服务器可以卸载,完美卸载SQL Server 2008的方法
  13. psp3000 刷机
  14. 清华大学计算机系招生数量,清华大学报考信息出炉,计算机报考人数最多,有些专业无人报考...
  15. MATLAB自带函数实现经验模态分解总结
  16. java zh_java 中zhis的用法
  17. 人脸识别概述-opencv中文文档
  18. java 获得唯一 数字_java生成唯一数字
  19. matlab共阳极二极管,三引脚SOT-323封装的共阳极稳压二极管详情
  20. 如何从高德获取地铁数据

热门文章

  1. Domain Driven Design(领域驱动设计)
  2. lvds输入悬空_LVDS技术原理及详细介绍
  3. 解决Microsoft Store应用商店打不开 代码: 0x80131500
  4. 第二章.物理层:2.6宽带接入技术
  5. DirectVobSub(VsFilter)的基本原理和实现实现
  6. 千锋云计算毕业设计论文:论文设计任务书
  7. 冻结Excel表格中前两行不滚动问题
  8. .Net Core开发学习(一) ——Startup 类
  9. 世界名著《读懂孩子心》的读后感范文3200字
  10. 中兴ZXVb860av2.1t刷机固件,芯片晶晨S905l-b,不失效线刷包,当贝桌面