2021SC@SDUSC
研究内容介绍本人负责的是负责的是将查询块QB转换成逻辑查询计划(OP Tree)
如下的代码出自apaceh-hive-3.1.2-src/ql/src/java/org/apache/hadoop/hive/ql/plan中,也就是我的分析目标代码。我们在Blog9-12中,完成了对如下文件代码的解析:
- BoundaryDef.java
- PTFexpressionDef.java
- OrderDef.java
- OrderexpressionDef.java
- PartitionDef.java
- WindowTableFunctionDef.java
- PartitionedTableFunctionDef.java
- PTFInputDef.java
- PTFQueryInputDef.java
- ShapeDetails.java
本周为最后一周的Blog,我们就接着往下继续研究,把ptf文件夹下的剩余代码解析完毕:
- WindowexpressionDef.java
- WindowframeDef.java
- WindowFunctionDef.java
我们首先附上整个java文件源码。
package org.apache.hadoop.hive.ql.plan.ptf;
import org.apache.hadoop.hive.ql.plan.Explain;
public abstract class WindowexpressionDef extends PTFexpressionDef {
private String alias;
@Explain(displayName = "alias")
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
}
开始解析。
全局变量解析(1)我们还是和以前一样,先从导入包的解析开始。我们首先查看导入包有哪些:
import org.apache.hadoop.hive.ql.plan.Explain;org.apache.hadoop.hive.ql.plan.Explain导入包解析
我们先到apache官网API接口上寻找对这个包的详细描述:
这是更像是一个类而不是一个标准包,与其他的导入包是有很大不同的,它里面并没有包含着其他的类。其中并没有方法,有的只是一些基本变量,还有引入了其他类的变量。同样的,我们等到需要使用到这个类的时候再返回这里进行详细的解析。
至此,我们已经解析了全部的导入包。
全局变量解析(2)我们再来看一下全局变量中有哪些:
private String alias;
这个是一个String类型的变量,是JAVA的最基础的类型,这里我们不做解析。
至此,我们解析了所有的全局变量,接下来我们开始对代码的解析。
Explain的设置@Explain(displayName = "alias")
这是一个什么样的语句?我们先来观看@Expain这个关键字。@Explain其实是在调用我们的导入包org.apache.hadoop.hive.ql.plan.Explain。而在这个包里,有着其string类型的displayName变量。前面的displayName = “alias"语句是在将变量displayName的值设置为"alias”。
方法getAlias @Explain(displayName = "alias")
public String getAlias() {
return alias;
}
}
对于参数alias的getter方法,用于得到alias参数。
方法setAlias public void setAlias(String alias) {
this.alias = alias;
}
对于参数alias的setter方法,用于设置alias参数。
至此,我们对WindowexpressionDef.java文件的代码解析全部完成,我们接着往下解析代码。
WindowframeDef.java文件代码解析我们首先附上整个java文件代码。
package org.apache.hadoop.hive.ql.plan.ptf;
import org.apache.hadoop.hive.common.classification.InterfaceAudience;
import org.apache.hadoop.hive.common.classification.InterfaceStability;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.parse.WindowingSpec.WindowType;
@InterfaceAudience.Public
@InterfaceStability.Stable
public class WindowframeDef {
private WindowType windowType;
private BoundaryDef start;
private BoundaryDef end;
private final int windowSize;
private OrderDef orderDef; // Order expressions which will only get set and used for RANGE windowing type
public WindowframeDef(WindowType windowType, BoundaryDef start, BoundaryDef end) {
this.windowType = windowType;
this.start = start;
this.end = end;
// Calculate window size
if (start.getDirection() == end.getDirection()) {
windowSize = Math.abs(end.getAmt() - start.getAmt()) + 1;
} else {
windowSize = end.getAmt() + start.getAmt() + 1;
}
}
public BoundaryDef getStart() {
return start;
}
public BoundaryDef getEnd() {
return end;
}
public WindowType getWindowType() {
return windowType;
}
public void setOrderDef(OrderDef orderDef) {
this.orderDef = orderDef;
}
public OrderDef getOrderDef() throws HiveException {
if (this.windowType != WindowType.RANGE) {
throw new HiveException("Order expressions should only be used for RANGE windowing type");
}
return orderDef;
}
public boolean isStartUnbounded() {
return start.isUnbounded();
}
public boolean isEndUnbounded() {
return end.isUnbounded();
}
public int getWindowSize() {
return windowSize;
}
@Override
public String toString() {
return windowType + " " + start + "~" + end;
}
}
开始解析。
全局变量解析(1)我们首先需要解析导入包以及设定的全局变量,这样方便我们的后续解析。我们首先先来看一下WindowframeDef.java文件导入了哪些包。
import org.apache.hadoop.hive.common.classification.InterfaceAudience; import org.apache.hadoop.hive.common.classification.InterfaceStability; import org.apache.hadoop.hive.ql.metadata.HiveException; import org.apache.hadoop.hive.ql.parse.WindowingSpec.WindowType;
我们现在开始解析这些包。
org.apache.hadoop.hive.common.classification.InterfaceAudience导入包解析还是和以前一样,我们到apache官网API上寻找对这个包的详细介绍:
可以看得出来,这是一个接口类的包。我们导入这个包的目的是为了可以直接引用实现其接口的类,从而实现只引用一个包但是可以调用多个方法的效果。我们再来看一下官方对这个包的描述:Annotation to inform users of a package, class or method’s intended audience.我们对其翻译得:通知用户包、类或方法的目标受众的注释。等待到需要调用其中的实现类中的变量以及方法后我们再进到具体的实现类去寻找:
还是和以前一样,我们到apache官网API上寻找对这个包的详细介绍:
这个包和上一个包是同样的类型,是一个接口类型包,我们调用这个包其实是在调用其实现的接口。我们来看一下其官方描述:Annotation to inform users of how much to rely on a particular package, class or method not changing over time.我们将其翻译得:注释通知用户在多大程度上依赖于不随时间变化的特定包、类或方法。等到调用到实现类其中的方法或变量时我们再进入具体的类中查看:
还是和以前一样,我们到apache官网API上寻找对这个包的详细介绍:
很显然,我们从这个类的名字就可以得知这是一个处理异常的类。而且我们查看官方对这个类的描述也可以得知这是一个异常类:Generic exception class for Hive.翻译即得:Hive的通用异常类。这个类导入到本文件中显然是处理异常的,防止程序崩溃。我们等待需要用到里面的异常捕获时再返回此处进行详细解析。
还是和以前一样,我们到apache官网API上寻找对这个包的详细介绍:
我们观察一下这个类发现它有一个枚举型数组,里面包含了RANGE以及ROWS两个实例化对象,这一点我们要注意,很有可能在下文中会设置或者引用它们。这个包的方法仅有两个,较为简单,等待至我们引用到方法的时候再回来此处做详细解析。
至此,我们对WindowframeDef.java文件的导入包解析已经全部完成,现在我们继续解析全局变量。
全局变量解析(2)我们来看一下开头定义的两个@是什么意思:
@InterfaceAudience.Public @InterfaceStability.Stable
很显然,这是在调用两个包的实现类。我们查看这两个实现类。
实现类InterfaceAudience.Public还是和以前一样,我们到apache官网API上寻找对这个包的详细介绍:
我们来看一下官方对其的描述:Intended for use by any project or application.翻译即得:打算由任何项目或应用程序使用。但是这里并没有提供什么其他的信息,我们就暂时不做解析。
还是和以前一样,我们到apache官网API上寻找对这个包的详细介绍:
我们来看一下官方对其的描述:Can evolve while retaining compatibility for minor release boundaries.; can break compatibility only at major release.翻译即得:可以在保持小版本边界兼容性的情况下进行演化。只会在主版本时破坏兼容性。但是这里并没有提供什么其他的信息,我们就暂时不做解析。
我们现在来看一下定义的全局变量有哪些:
private WindowType windowType; private BoundaryDef start; private BoundaryDef end; private final int windowSize; private OrderDef orderDef; // Order expressions which will only get set and used for RANGE windowing type
WindowType类型的windowType,这是我们的导入包org.apache.hadoop.hive.ql.parse.WindowingSpec.WindowType的一个实例化对象。
BoundarDef类型的start以及end,这是一个在ptf文件夹下的一个类:
final int 类型的windowSize,这是一个基础的java类型,我们不做解析。
OrderDef类型的orderDef,这是一个在ptf文件夹下的一个类:
public WindowframeDef(WindowType windowType, BoundaryDef start, BoundaryDef end) {
this.windowType = windowType;
this.start = start;
this.end = end;
// Calculate window size
if (start.getDirection() == end.getDirection()) {
windowSize = Math.abs(end.getAmt() - start.getAmt()) + 1;
} else {
windowSize = end.getAmt() + start.getAmt() + 1;
}
}
这个方法的开头是一个setter的过程,我们不做解析。我们看后面的if语句。start.getDirection()方法是什么方法?我们回到BoundaryDef.java文件中查看源代码:
public Direction getDirection() {
return direction;
}
这个direction是什么?这是一个全局变量,我们查看其定义语句:
Direction direction;
而这个Direciton类型是一个导入包:org.apache.hadoop.hive.ql.parse.WindowingSpec.Direction,如果我们需要用到这个导入包的话我们再进行详细的解析.
回到源码中来,我们查看一下if的判断条件:start.getDirection()是否与end.getDirection()相等,如果是则执行语句windowSize = Math.abs(end.getAmt() - start.getAmt()) + 1;,这个Math,abs()是什么方法?经过查阅资料,这是一个取绝对值的方法.而在里面的getAmt()又是一个什么方法?我们又回到BoundaryDef.java文件中查看源代码:
public int getAmt() {
return amt;
}
而amt是全局变量,我们查看其定义语句:
private int amt;
那么语句windowSize = Math.abs(end.getAmt() - start.getAmt()) + 1;的意思就是将wndowSize的值设置为end的amt变量以及start.amt变量之差取正值再加1.
如果if判断为false则将windowSize的值设置为end的amt与start的amt之和再加1.
方法getStart public BoundaryDef getStart() {
return start;
}
对于参数start的getter方法,用于得到start参数。
方法getEnd public BoundaryDef getEnd() {
return end;
}
对于参数end的getter方法,用于得到end参数。
方法getWindowType在这里插入代码片 public WindowType getWindowType() {
return windowType;
}
对于参数windowType的getter方法,用于得到windowType参数。
方法setOrderDef public void setOrderDef(OrderDef orderDef) {
this.orderDef = orderDef;
}
对于参数orderDef的setter方法,用于设置orderDef参数。
方法getOrderDef public OrderDef getOrderDef() throws HiveException {
if (this.windowType != WindowType.RANGE) {
throw new HiveException("Order expressions should only be used for RANGE windowing type");
}
return orderDef;
}
这是一个异常相关的方法,首先用if语句判断本实例化对象的windowType变量是否与WindowType中的枚举型变量RANGE是否不等,如果不等则抛出异常,并打印出Order expressions should only be used for RANGE windowing type语句,如果相等则直接返回orderDef.这是一个对于参数orderDef的getter方法,用于得到orderDef参数,但是加了一层保护机制,防止引用错误的orderDef参数.
方法isStartUnbounded public boolean isStartUnbounded() {
return start.isUnbounded();
}
我们来看一下BoundaryDef.java文件中的isUnbounded方法源代码:
public boolean isUnbounded() {
return this.getAmt() == BoundarySpec.UNBOUNDED_AMOUNT;
}
这个方法是判断实例化的BoundaryDef对象的amt值是否与BoundarySpec.UNBOUNDED_AMOUNT即BoundarySpec中的全局变量UNBOUNDED_AMOUNT相等,然后返回boolean值.而BoundartSpec是BoundaryDef.java文件中的导入包org.apache.hadoop.hive.ql.parse.WindowingSpec.BoundarySpec,这个包其中有一个全局变量UNBOUNDED_AMOUNT,其类型为int类型.
方法isEndUnbounded public boolean isEndUnbounded() {
return end.isUnbounded();
}
这个方法和上个方法是一样的,这里我们不再赘述.
方法getWindowSize public int getWindowSize() {
return windowSize;
}
对于参数windowSiz的getter方法,用于得到windowSiz参数。
(重写)方法toString @Override
public String toString() {
return windowType + " " + start + "~" + end;
}
这里重写了每个类的默认方法toString,改为了windowType + " " + start + "~" + end的格式,也就是以字符串形式返回各个值.
至此,我们对WindowframeDef.java文件代码解析全部完成,我们接着往下解析代码.
WindowFunctionDef.java文件代码解析我们首先附上整个java文件代码
package org.apache.hadoop.hive.ql.plan.ptf;
import java.util.ArrayList;
import java.util.List;
import org.apache.hadoop.hive.ql.plan.Explain;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFevaluator;
@Explain(displayName = "window function definition")
public class WindowFunctionDef extends WindowexpressionDef {
String name;
boolean isStar;
boolean isDistinct;
List args;
WindowframeDef windowframe;
GenericUDAFevaluator wFneval;
boolean pivotResult;
@Explain(displayName = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Explain(displayName = "isStar", displayonlyOnTrue = true)
public boolean isStar() {
return isStar;
}
public void setStar(boolean isStar) {
this.isStar = isStar;
}
@Explain(displayName = "isDistinct", displayonlyOnTrue = true)
public boolean isDistinct() {
return isDistinct;
}
public void setDistinct(boolean isDistinct) {
this.isDistinct = isDistinct;
}
public List getArgs() {
return args;
}
public void setArgs(List args) {
this.args = args;
}
public void addArg(PTFexpressionDef arg) {
args = args == null ? new ArrayList() : args;
args.add(arg);
}
@Explain(displayName = "arguments")
public String getArgsExplain() {
if (args == null) {
return null;
}
StringBuilder builder = new StringBuilder();
for (PTFexpressionDef expression : args) {
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(expression.getExprNode().getExprString());
}
return builder.toString();
}
public WindowframeDef getWindowframe() {
return windowframe;
}
public void setWindowframe(WindowframeDef windowframe) {
this.windowframe = windowframe;
}
@Explain(displayName = "window frame")
public String getWindowframeExplain() {
return windowframe == null ? null : windowframe.toString();
}
public GenericUDAFevaluator getWFneval() {
return wFneval;
}
public void setWFneval(GenericUDAFevaluator wFneval) {
this.wFneval = wFneval;
}
@Explain(displayName = "window function")
public String getWFnevalExplain() {
return wFneval == null ? null : wFneval.getClass().getSimpleName();
}
@Explain(displayName = "isPivotResult", displayonlyOnTrue = true)
public boolean isPivotResult() {
return pivotResult;
}
public void setPivotResult(boolean pivotResult) {
this.pivotResult = pivotResult;
}
}
开始解析.
全局变量解析(1)我们还是和以前一样,先从导入包的解析开始。我们首先查看导入包有哪些:
import java.util.ArrayList; import java.util.List; import org.apache.hadoop.hive.ql.plan.Explain; import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFevaluator;
对于前两个包ArrayList以及List作为java最基础的变量,我们在这里不做解析.
org.apache.hadoop.hive.ql.plan.Explain导入包解析我们在开头就已经对这个包解析过了,所以我们在这里不再赘述.
org.apache.hadoop.hive.ql.udf.generic.GenericUDAFevaluator导入包解析还是和以前一样,我们到apache官网API上寻找对这个包的详细介绍:
这是一个聚合了很多方法和变量的类,我们不对其中的方法做一个个解析,当我们需要用到其中的方法时再回来做具体的解析.
我们现在来看一下定义的全局变量有哪些:
String name; boolean isStar; boolean isDistinct; Listargs; WindowframeDef windowframe; GenericUDAFevaluator wFneval; boolean pivotResult;
对于String类型的name,以及boolean类型的isStar和isDistinct以及pivotResult,还有List类型的args均为java最基础的变量类型,在这里我们不做解析
对于WindowframeDef类型的windowframe,这是在ptf文件夹下的一个类:
还有GenericUDAFevaluator类型的wFneval则是导入包org.apache.hadoop.hive.ql.udf.generic.GenericUDAFevaluator引入的变量类型.
此外,在List类型的args里面包含的元素变量类型为PTFexpressionDef类,这也是在ptf文件夹下的一个类:
至此,全局变量全部解析完毕,我们开始解析代码.
@Explain(displayName = "name")
这是一个什么样的语句?我们先来观看@Expain这个关键字。@Explain其实是在调用我们的导入包org.apache.hadoop.hive.ql.plan.Explain。而在这个包里,有着其string类型的displayName变量。前面的displayName = “name"语句是在将变量displayName的值设置为"name”。
参数name的getter与setter方法 @Explain(displayName = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
对于参数name的getter与setter方法,用于得到和设置name参数。
Explain设置(2)@Explain(displayName = "isStar", displayonlyOnTrue = true)
这次的Explain设置则是将变量displayName的值设置为"isStar",以及设置Explain中的全局boolean类型变量displayOnlyOnTrue为true.
参数isStar的getter与setter方法 @Explain(displayName = "isStar", displayonlyOnTrue = true)
public boolean isStar() {
return isStar;
}
public void setStar(boolean isStar) {
this.isStar = isStar;
}
对于参数isStar的getter与setter方法,用于得到和设置isStar参数。
Explain设置(3)@Explain(displayName = "isDistinct", displayonlyOnTrue = true)
这次的Explain设置则是将变量displayName的值设置为"isDistinct",以及设置Explain中的全局boolean类型变量displayOnlyOnTrue为true.
参数isDistinct的getter与setter方法 @Explain(displayName = "isDistinct", displayonlyOnTrue = true)
public boolean isDistinct() {
return isDistinct;
}
public void setDistinct(boolean isDistinct) {
this.isDistinct = isDistinct;
}
对于参数isDistinct的getter与setter方法,用于得到和设置isDistinct参数。
参数args的getter与setter方法public ListgetArgs() { return args; }
public void setArgs(Listargs) { this.args = args; }
对于参数args的getter与setter方法,用于得到和设置args参数。
方法addArg public void addArg(PTFexpressionDef arg) {
args = args == null ? new ArrayList() : args;
args.add(arg);
}
我们来看第一句:args = args == null ? new ArrayList
@Explain(displayName = "arguments")
这次的Explain设置则是将变量displayName的值设置为"arguments".
方法getArgsExplain @Explain(displayName = "arguments")
public String getArgsExplain() {
if (args == null) {
return null;
}
StringBuilder builder = new StringBuilder();
for (PTFexpressionDef expression : args) {
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(expression.getExprNode().getExprString());
}
return builder.toString();
}
首先是if判断语句,如果args是null的话就直接返回null了.如果不是null就接着往下走,建造一个StringBuilder,这个类型的变量可以理解为字符串缓冲区,可以往里面添加字符串然后到时候会自动拼接起来.for循环则遍历args里面的所有元素,并将这个元素赋值给expression临时变量.接着if语句判断这个builder有没有被使用过,如果使用过则自动添加一个逗号用于分隔元素.然后就添加expression.getExprNode().getExprString()的返回值.这是什么函数?我们其实在之前就已经写过了,这里我们直接引用:
public WindowframeDef getWindowframe() {
return windowframe;
}
public void setWindowframe(WindowframeDef windowframe) {
this.windowframe = windowframe;
}
对于参数windowframe的getter与setter方法,用于得到和设置windowframe参数。
Explain设置(5)@Explain(displayName = "window frame")
这次的Explain设置则是将变量displayName的值设置为"window frame".
方法getWindowframeExplain @Explain(displayName = "window frame")
public String getWindowframeExplain() {
return windowframe == null ? null : windowframe.toString();
}
返回语句中,先判断windowframe是否为null,如果是则返回null值,如果不是则返回String类型的windowframe.
参数wFneval的getter与setter方法 public GenericUDAFevaluator getWFneval() {
return wFneval;
}
public void setWFneval(GenericUDAFevaluator wFneval) {
this.wFneval = wFneval;
}
对于参数wFneval的getter与setter方法,用于得到和设置wFneval参数。
Explain设置(6)@Explain(displayName = "window function")
这次的Explain设置则是将变量displayName的值设置为"window function".
方法getWFnevalExplain @Explain(displayName = "window function")
public String getWFnevalExplain() {
return wFneval == null ? null : wFneval.getClass().getSimpleName();
}
这里的return语句中也是先判断wFneval变量是否为null值,如果是则返回null如果不是则返回wFneval.getClass().getSimpleName()的值.我们先来看一下getClass()方法,他是一个返回一个运行的Class类型对象.就是说这个东西是在哪个类运行的,在这里返回的就是org.apache.hadoop.hive.ql.udf.generic.GenericUDAFevaluator然后调用里面的getSimpleName()方法.而这个方法是用于返回源代码中给定的基础类的简单名称,也就是GenericUDAFevaluator.
Explain设置(7)@Explain(displayName = "isPivotResult", displayonlyOnTrue = true)
这次的Explain设置则是将变量displayName的值设置为"isPivotResult",以及设置Explain中的全局boolean类型变量displayOnlyOnTrue为true.
参数pivotResult的getter与setter方法 @Explain(displayName = "isPivotResult", displayonlyOnTrue = true)
public boolean isPivotResult() {
return pivotResult;
}
public void setPivotResult(boolean pivotResult) {
this.pivotResult = pivotResult;
}
对于参数pivotResult的getter与setter方法,用于得到和设置pivotResult参数。
至此,我们完成了对WindowFunctionDef.java文件的全部代码解析,也完成了对ptf文件夹下的全部代码解析.
小结本次Blog为最后一次Blog,我们一共完成了两个文件夹下的全部代码:mapper文件夹以及ptf文件夹.我们在解析代码的过程中不仅学习到了Hive的相关底层逻辑知识,也学习到了很多关于Java方面的知识,是一次非常收获满满的学习过程.



