mondrian 源码解读(四)-从parser开始

前面几节我们介绍了一下mondrian最基础的东西,只有在了解了这些后,我们才能继续往下看。

首先,mondrian需要将mdx语句解析成java对象,我们先看看他是如何解析的。

Query创建过程

我们从ConnectionBase的parser开始,在parseStatement中,有一句很重要的话语句。

public Query parseQuery(String query) {
    return (Query) parseStatement(query);
}

Query parseStatement(...){
...
return parser.parseInternal(
                statement, query, debug, funTable, strictValidation);
...
}

一,创建解析器

首先,我们需要一个parser。在ConnectionBase类中,我们看到了parser的创建过程:

protected MdxParserValidator createParser() {
    return true
        ? new JavaccParserValidatorImpl()
        : new MdxParserValidatorImpl();
}

实际上,由于这里永远是true,所以返回的是JavaccParserValidatorImpl。这个类也是实现了MdxParserValidator。它与原生的javaccparser生成的MdxParserValidatorImpl的主要不同在于factory的创建方式和异常的处理方式,这里就不做过多解读了,具体请看源代码。

二,解析mdx

获取Parser后,通过调用parser的parseInternal方法,获取到一个QueryPart。这是如何解析的呢?在mondrian/parser目录下,有个MdxParser.jj的文件。这个文件是用javacc的语法来写的,如果不懂javacc的语法,可以移步到网上看看:http://java.net/projects/javacc/,这里就不展开了。


QueryPart parseInternal(
        Statement statement,
        String queryString,
        boolean debug,
        FunTable funTable,
        boolean strictValidation);

QueryPart类

QueryPart是什么呢?我们看下图,他的主要功能是获取子节点。于是我们知道,任何具有子节点的对象都可以是一个QueryPart,右边框框中框出来所有的继承关系。

在parseInternal中产生的是那个对象什么呢?通过DEBUG代码后,我们可以看到是Query对象,即mdx语句被转换成了Query。
QueryPart

Query初始化

我们先看看他的构造方法:

/**
 * Creates a Query.
 */
public Query(
    Statement statement,
    Cube mdxCube,
    Formula[] formulas,
    QueryAxis[] axes,
    QueryAxis slicerAxis,
    QueryPart[] cellProps,
    Parameter[] parameters,
    boolean strictValidation)
{
    this.statement = statement;
    this.cube = mdxCube;
    this.formulas = formulas;
    this.axes = axes;
    normalizeAxes();
    this.slicerAxis = slicerAxis;
    this.cellProps = cellProps;
    this.parameters.addAll(Arrays.asList(parameters));
    this.measuresMembers = new HashSet<Member>();
    // assume, for now, that cross joins on virtual cubes can be
    // processed natively; as we parse the query, we'll know otherwise
    this.nativeCrossJoinVirtualCube = true;
    this.strictValidation = strictValidation;
    this.alertedNonNativeFunDefs = new HashSet<FunDef>();
    statement.setQuery(this);
    resolve();

    if (RolapUtil.PROFILE_LOGGER.isDebugEnabled()
        && statement.getProfileHandler() == null)
    {
        statement.enableProfiling(
            new ProfileHandler() {
                public void explain(String plan, QueryTiming timing) {
                    if (timing != null) {
                        plan += "\n" + timing;
                    }
                    RolapUtil.PROFILE_LOGGER.debug(plan);
                }
            }
        );
    }
}

resolve

在上面中,我们着重看resolve方法。
这个是完成Query功能最重要的地方,在这里,query完成了它自己的构建,包括类型检测,UnresolvedFunCall对象到ResolvedFunCall对象的转化,添加测量成员,创建查询轴计算器,切片轴计算器等。

  1. 创建校验器
  2. 解析自己和子节点
  3. 创建编译器
  4. 编译
public void resolve() {
    final Validator validator = createValidator(); // 1
    resolve(validator); // 2 resolve self and children
    // Create a dummy result so we can use its evaluator
    final Evaluator evaluator = RolapUtil.createEvaluator(statement);
    ExpCompiler compiler =
        createCompiler(
            evaluator, validator, Collections.singletonList(resultStyle));//3
    compile(compiler);//4
}

resolve(validator)

我们看第2步,解析自己和子节点,这一步最重要的就是将UnresolvedFunCall对象转化为ResolvedFunCall对象

  1. 首先构建formulas的内部成员,具体请看formula.createElement(validator.getQuery());方法,这里就不展开了。
  2. 注册参数
  3. 注册别名表达式
  4. 校验formulas,主要是将formula中的id解析成表达式对象
  5. 校验查询轴和切片轴,在此过程中会获取查询轴计算器和切片轴计算器
  6. 校验一个层次只能存在一个轴上
/**
 * Performs type-checking and validates internal consistency of a query.
 *
 * @param validator Validator
 */
public void resolve(Validator validator) {
// Before commencing validation, create all calculated members,
// calculated sets, and parameters.
if (formulas != null) {
    // Resolving of formulas should be done in two parts
    // because formulas might depend on each other, so all calculated
    // mdx elements have to be defined during resolve.
    for (Formula formula : formulas) {
        formula.createElement(validator.getQuery());
    }
}

// Register all parameters.
parameters.clear();
parametersByName.clear();
accept(new ParameterFinder());

// Register all aliased expressions ('expr AS alias') as named sets.
accept(new AliasedExpressionFinder());

// Validate formulas.
if (formulas != null) {
    for (Formula formula : formulas) {
        validator.validate(formula);
    }
}

// Validate axes.
if (axes != null) {
    Set<Integer> axisNames = new HashSet<Integer>();
    for (QueryAxis axis : axes) {
        validator.validate(axis);
        if (!axisNames.add(axis.getAxisOrdinal().logicalOrdinal())) {
            throw MondrianResource.instance().DuplicateAxis.ex(
                axis.getAxisName());
        }
    }

    // Make sure that there are no gaps. If there are N axes, then axes
    // 0 .. N-1 should exist.
    int seekOrdinal =
        AxisOrdinal.StandardAxisOrdinal.COLUMNS.logicalOrdinal();
    for (QueryAxis axis : axes) {
        if (!axisNames.contains(seekOrdinal)) {
            AxisOrdinal axisName =
                AxisOrdinal.StandardAxisOrdinal.forLogicalOrdinal(
                    seekOrdinal);
            throw MondrianResource.instance().NonContiguousAxis.ex(
                seekOrdinal,
                axisName.name());
        }
        ++seekOrdinal;
    }
}
if (slicerAxis != null) {
    slicerAxis.validate(validator);
}

// Make sure that no hierarchy is used on more than one axis.
for (Hierarchy hierarchy : ((RolapCube) getCube()).getHierarchies()) {
    int useCount = 0;
    for (QueryAxis axis : allAxes()) {
        if (axis.getSet().getType().usesHierarchy(hierarchy, true)) {
            ++useCount;
        }
    }
    if (useCount > 1) {
        throw MondrianResource.instance().HierarchyInIndependentAxes.ex(
            hierarchy.getUniqueName());
    }
}
}

compile(compiler)

然后我们看第4步,这步主要是创建查询轴计算器,切片轴计算器等。

/**
 * Generates compiled forms of all expressions.
 *
 * @param compiler Compiler
 */
private void compile(ExpCompiler compiler) {
    if (formulas != null) {
        for (Formula formula : formulas) {
            formula.compile();
        }
    }

    if (axes != null) {
        axisCalcs = new Calc[axes.length];
        for (int i = 0; i < axes.length; i++) {
            axisCalcs[i] = axes[i].compile(compiler, resultStyle);
        }
    }
    if (slicerAxis != null) {
        slicerCalc = slicerAxis.compile(compiler, resultStyle);
    }
}