【软件工程实践】Hive研究-Blog10
2021SC@SDUSC
研究内容介绍
本人负责的是负责的是将查询块QB转换成逻辑查询计划(OP Tree) 如下的代码出自apaceh-hive-3.1.2-src/ql/src/java/org/apache/hadoop/hive/ql/plan中,也就是我的分析目标代码。之前的Hive研究-Blog9中已经解析了BoundartDef.java文件的代码以及PTFExpressionDef.java文件的代码,本周的任务就接着解析ptf文件夹下的文件源码,先从OrderDef.java文件开始。
OrderDef.java文件代码解析
我们首先附上整个java文件代码
package org.apache.hadoop.hive.ql.plan.ptf;
import java.util.ArrayList;
import java.util.List;
public class OrderDef {
List<OrderExpressionDef> expressions;
public OrderDef() {}
public OrderDef(PartitionDef pDef) {
for(PTFExpressionDef eDef : pDef.getExpressions()) {
addExpression(new OrderExpressionDef(eDef));
}
}
public List<OrderExpressionDef> getExpressions() {
return expressions;
}
public void setExpressions(ArrayList<OrderExpressionDef> expressions) {
this.expressions = expressions;
}
public void addExpression(OrderExpressionDef e) {
expressions = expressions == null ? new ArrayList<OrderExpressionDef>() : expressions;
expressions.add(e);
}
}
开始解析。
类构造方法OrderDef(1)
public OrderDef() {}
public OrderDef(PartitionDef pDef) {
for(PTFExpressionDef eDef : pDef.getExpressions()) {
addExpression(new OrderExpressionDef(eDef));
}
}
我们可以看到,对于OrderDef类有两个构造方法,第一个是空的构造方法,而第二个是含参的构造方法。我们首先看一下传入的参数类型:ParitionDef类。这是一个什么类型的类?我们先从ptf文件下搜寻,看看是否存在这个类。果不其然,我们找到了目标类。 在本次的方法调用中,使用到了PartitionDef类的方法getExpressions。这是一个什么方法呢?我们直接调取PartitionDef.java文件的源码位置进行解析。
方法getExpressions
public List<PTFExpressionDef> getExpressions() {
return expressions;
}
看来这是一个getter方法。我们看一下返回的变量为expressions,是PartitionDef类自带的变量。我们在这里给出它的构造语句:
private List<PTFExpressionDef> expressions;
可以看得出来,这是一个列表类型的变量,而列表里面的元素类型为PTFExpressionDef类型。同样的,我们先在ptf文件夹下寻找这个类。果不其然,我们找到了它: 由于在这里并没有调用到其内容的方法,我们等待至调用的时候再进入到该文件中去寻找具体实现细节进行解析。
类构造方法OrderDef(2)
public OrderDef(PartitionDef pDef) {
for(PTFExpressionDef eDef : pDef.getExpressions()) {
addExpression(new OrderExpressionDef(eDef));
}
}
我们回到源码中来。刚才已经介绍了这个getExpression方法,它是一个getter方法,用于得到一个自带的变量。接下来的addExpression方法又是一个什么方法?我们浏览上下文,在文件的最后发现了对这个方法的定义,我们先来看一下该方法的具体细节。
方法addExpression
public void addExpression(OrderExpressionDef e) {
expressions = expressions == null ? new ArrayList<OrderExpressionDef>() : expressions;
expressions.add(e);
}
在这里,传入了一个类型为OrderExpressionDef的变量。还是老样子,我们先从ptf文件夹下寻找该类,果然又找到了这个类: 等至需要使用到该类的内部方法或变量时我们再进行详细的剖析。
回到源码中来,接下来的时一个条件赋值语句。首先判断变量expressions是否为空。这个expressions变量是什么变量?我们浏览上文,发现这是一个全局变量,我们给出其定义语句:List<OrderExpressionDef> expressions; 这是一个列表类型的变量,而列表内的元素类型为OrderExpressionDef类型。如果expressions为空,那么就会将expressions设置为一个新的ArrayList<>类型的变量,其中列表内部元素的类型为OrderExpressionDef。如果expressions不为空,则将传入的参数e加入到expressions中,这个add方法是List类型自带的方法,其方法的作用是向这个列表添加新的元素。
整个addExpression方法的作用就是判断列表类型变量expressions是否为空,如果不为空则把传入的变量加入到其中。
类构造方法OrderDef(3)
public OrderDef(PartitionDef pDef) {
for(PTFExpressionDef eDef : pDef.getExpressions()) {
addExpression(new OrderExpressionDef(eDef));
}
}
我们在(1)以及(2)中已经解释了getExpressions方法以及addExpression方法,现在来解释一下方法addExpression传入的参数时调用的方法。从名字上可以很容易的看出这是一个构造方法,我们查阅相关代码来进行解释。
类构造方法OrderExpressionDef
我们来到OrderExpressionDef.java文件中来,找到这个构造方法。
public OrderExpressionDef() {}
首先时一个空的构造方法,这是不带参数的,不是我们在上面调用的含参构造。
public OrderExpressionDef(PTFExpressionDef e) {
super(e);
order = Order.ASC;
nullOrder = NullOrder.NULLS_FIRST;
}
显然,这就是我们需要解释的构造方法了。我们先来看一下这个super()方法。在构造器方法中调用super方法,那么很有可能这个类是一个子类。我们查看OrderExpressionDef类的开头声明,发现了这样一个语句public class OrderExpressionDef extends PTFExpressionDef 确定了它确实是一个子类。如果一个类中没有写任何的构造方法,JVM会生成一个默认的无参构造方法。在继承关系中,由于在子类的构造方法中,第一条语句默认为调用父类的无参构造方法(即默认为super(),一般这句话省略了)。所以当在父类中定义了有参构造函数,都是没有定义无参构造函数时,IDE会强制要求我们定义一个相同参数类型的构造器。因此,我们要在子类的构造方法中加入super()方法,来防止编译错误。
回到源码中,我们来看一下接下来的两个赋值语句:
order = Order.ASC;
nullOrder = NullOrder.NULLS_FIRST;
这是我们没有见过的赋值方式。
我们猜测:ASC以及NULLS_FIRST是排序的方式,也就说重新调整将值进行排序的设定。ASC就是降序,而NULLS_FIRST就是将NULL的排序位置设置为靠前。因此Order.ASC意思就是将内部的内容设置为降序的序列,而NullOrder.NULLS_FIRST则是将Null值的元素的排序靠前。
但是经过查阅资料后发现,ASC以及NULLS_FIRST其实是开头引入的两个类中的枚举型变量的其中一个,也就是说,这两句赋值语句是将已经实例化的对象赋予他们。具体的内容我们放到下文中进行解释。
我们的猜测出现了错误,还有着比较大的偏差,这告诉了我们代码必须要在合适的地方写上注释,否则就会出现误解的意思从而导致错误的使用。
至此,整个构造语句已完成。
类构造方法OrderDef(4)
public OrderDef(PartitionDef pDef) {
for(PTFExpressionDef eDef : pDef.getExpressions()) {
addExpression(new OrderExpressionDef(eDef));
}
}
回到源码中,我们再解释了最后一个构造器方法OrderExpressionDef后,整个类构造方法也全部解释完毕了。首先是调用getExpressions()方法,得到一个列表,而列表里面的变量类型为PTFExpressionsDef类型,接着调用添加元素的方法addExpression,给变量expressions添加元素或者初始化,而加入的元素就为新构造的OrderExpressionDef类型的变量,而构造需要的参数就是传入的参数。
方法getExpressions
public List<OrderExpressionDef> getExpressions() {
return expressions;
}
很明显,这是一个java中典型的getter方法,该方法的目的是得到全局变量expressions。
方法setExpressions
public void setExpressions(ArrayList<OrderExpressionDef> expressions) {
this.expressions = expressions;
}
同样的,这是对于参数expressions的setter方法,用于设置参数的值。
至此,我们对OrderDef.java文件的代码全部解析完毕,我们接着往下解析。
OrderExpressionDef.java文件代码解析
我们首先附上整个java文件代码。
package org.apache.hadoop.hive.ql.plan.ptf;
import org.apache.hadoop.hive.ql.parse.PTFInvocationSpec.NullOrder;
import org.apache.hadoop.hive.ql.parse.PTFInvocationSpec.Order;
public class OrderExpressionDef extends PTFExpressionDef {
private Order order;
private NullOrder nullOrder;
public OrderExpressionDef() {}
public OrderExpressionDef(PTFExpressionDef e) {
super(e);
order = Order.ASC;
nullOrder = NullOrder.NULLS_FIRST;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public NullOrder getNullOrder() {
return nullOrder;
}
public void setNullOrder(NullOrder nullOrder) {
this.nullOrder = nullOrder;
}
}
对于构造方法OrderExpressionDef,我们在上文就已经介绍过了,因此在这里我们不再进行赘述。
全局变量分析
我们还是先从全局变量入手分析。我们先来看一下全局变量有哪些:
private Order order;
private NullOrder nullOrder;
我们先来看一下Order是一个什么样的类?我们还是先从ptf文件夹下寻找,看一下是否存在这个类。浏览发现并不存在,因此我们直接进入到apache的官网寻找,找到了官方的API解释。 我们重点来看一下枚举型常量的描述: 这说明Order里面已经设置好了两个实例,一个是ASC,一个是DESC。 我们再来看一下两个方法,一个是valueof(String name),一个是values()。
方法valueof
我们从方法的注释中就可以看出,该方法相当于是枚举型集合中的特定参数的getter方法,也就是说我们输入想要取得的实例的名称,就会得到我们指定的实例对象。在本文件的构造函数中,语句order = Order.ASC; 的作用与该方法是类似的。
方法values
我们从方法的注释中也得知了该方法是取得枚举型中所有的实例化对象,返回的是一个数组,类型为Order类型。
接下来我们继续查看NullOrder是一个什么样的类。同样的,我们到apache的官方API上找到了具体的解释页面:
我们发现枚举型变量有如下两个: 我们来看一下方法。
方法valueOf
此方法和Order类的方法中同名方法一样的作用,故不赘述。
方法values
此方法和Order类的方法中同名方法一样的作用,故不赘述。
至此,我们的全局变量的解析已经全部完成,现在可以正式开始解析。
参数order的getter与setter方法
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
对于参数order的getter与setter方法,用于得到和设置order参数。
参数nullOrder的getter与setter方法
public NullOrder getNullOrder() {
return nullOrder;
}
public void setNullOrder(NullOrder nullOrder) {
this.nullOrder = nullOrder;
}
对于参数nullOrder的getter与setter方法,用于得到和设置nullOrder参数。
至此,对于OrderExpressionDef.java文件代码全部解析完毕,我们接着往下解析。
PartitionDef.java文件代码解析
我们首先附上整个文件代码。
package org.apache.hadoop.hive.ql.plan.ptf;
import java.util.ArrayList;
import java.util.List;
public class PartitionDef {
private List<PTFExpressionDef> expressions;
public List<PTFExpressionDef> getExpressions() {
return expressions;
}
public void setExpressions(List<PTFExpressionDef> expressions) {
this.expressions = expressions;
}
public void addExpression(PTFExpressionDef e) {
expressions = expressions == null ? new ArrayList<PTFExpressionDef>() : expressions;
expressions.add(e);
}
}
参数expressions的getter与setter方法
public List<PTFExpressionDef> getExpressions() {
return expressions;
}
public void setExpressions(List<PTFExpressionDef> expressions) {
this.expressions = expressions;
}
对于参数expressions的getter与setter方法,用于得到和设置expressions参数。
方法addExpression
public void addExpression(PTFExpressionDef e) {
expressions = expressions == null ? new ArrayList<PTFExpressionDef>() : expressions;
expressions.add(e);
}
这个方法与OrderDef.java中同名的方法十分的类似,不过传入的参数类型不同。我们可以看一下在OrderDef.java文件中,该方法的内容:
public void addExpression(OrderExpressionDef e) {
expressions = expressions == null ? new ArrayList<OrderExpressionDef>() : expressions;
expressions.add(e);
}
我们可以看到,在OrderDef.java中的addExpression方法传入的参数是OrderExpressionDef类型的参数,而在本类中的参数是PTFExpressionDef类型的,我们又在上文已经说明了OrderExpressionDef是PTFExpressionDef的子类,故类型差异其实是不存在的,可以理解为同一类型的传参,因此他们两个方法可以说是一样的,故不再此赘述。
我们接着往下解析
WindowTableFunctionDef.java文件代码解析
package org.apache.hadoop.hive.ql.plan.ptf;
import org.apache.hadoop.hive.ql.plan.Explain;
import java.util.List;
@Explain(displayName = "Windowing table definition")
public class WindowTableFunctionDef extends PartitionedTableFunctionDef {
List<WindowFunctionDef> windowFunctions;
int rankLimit = -1;
int rankLimitFunction;
@Explain(displayName = "window functions")
public List<WindowFunctionDef> getWindowFunctions() {
return windowFunctions;
}
public void setWindowFunctions(List<WindowFunctionDef> windowFunctions) {
this.windowFunctions = windowFunctions;
}
public int getRankLimit() {
return rankLimit;
}
public void setRankLimit(int rankLimit) {
this.rankLimit = rankLimit;
}
public int getRankLimitFunction() {
return rankLimitFunction;
}
public void setRankLimitFunction(int rankLimitFunction) {
this.rankLimitFunction = rankLimitFunction;
}
}
全局变量解析
老样子,我们先来看一下全局变量内容。
List<WindowFunctionDef> windowFunctions;
int rankLimit = -1;
int rankLimitFunction;
首先是这个List类型的windowFunctions,其中的元素是WindowFunctionDef类型,这是一个什么样的类?我们还是先看ptf文件夹下有没有这个类。我们在文件夹下中找到了这个类: 由于还未涉及到这个类的变量以及内部方法,故我们等到需要使用的时候再进入到具体的代码中进行详细的解析即可。
接着是这个rankLimt以及rankLimitFunction,它们都是int类型的参数,没有新的类型引入。
我们再来看一下引入的包:org.apache.hadoop.hive.ql.plan.Explain; 我们直接上apache官网API上进行查找,找到了对于Explain类的官方解析: 我们可以看到这个类中有一个string类型的变量,名字为displayName。而这个displayName是我们在下文使用到的一个变量。displayName的初始值设置为空字符串,而非NULL,这一点是需要注意的。对于其余变量,比如说displayOnlyOnTrue,explainLevels还有jsonOnly等变量,我们在下文暂时还未使用到,故我们并不对其进行详细解析。
至此,所有准备工作已完成,我们可以开始解析整个函数。
Explain类中displayName变量值设置(1):
@Explain(displayName = "Windowing table definition")
我们在上文已经解释过Explain是一个什么样的类了,也知道了displayName是其内部定义的变量,类型为String类型。这个语句是设置Explain类中的displayName为字符串"Windowing table definition"。
Explain类中displayName变量值设置(2):
@Explain(displayName = "window functions")
显而易见,这是设置Explain类中的displayName为字符串"window functions"。
变量windowFunctions的getter与setter方法
public List<WindowFunctionDef> getWindowFunctions() {
return windowFunctions;
}
public void setWindowFunctions(List<WindowFunctionDef> windowFunctions) {
this.windowFunctions = windowFunctions;
}
对于参数windowFunctions的getter与setter方法,用于得到和设置windowFunctions参数。
变量rankLimit的getter与setter方法
public int getRankLimit() {
return rankLimit;
}
public void setRankLimit(int rankLimit) {
this.rankLimit = rankLimit;
}
对于参数rankLimit的getter与setter方法,用于得到和设置rankLimit参数。
变量rankLimitFunction的getter与setter方法
public int getRankLimitFunction() {
return rankLimitFunction;
}
public void setRankLimitFunction(int rankLimitFunction) {
this.rankLimitFunction = rankLimitFunction;
}
对于参数rankLimitFunction的getter与setter方法,用于得到和设置rankLimitFunction参数。
至此,WindowTableFunctionDef.java文件已经全部解析完毕。
小结
本周的学习主要是在认识Hive的各种包,以及为下一周的解析做准备,提前认识各种参数变量以及方法,方便在下一周的学习更加方便的解析。希望在下一周的学习中能够继续学习到新的知识。
|