啥是Expression?
在sql语句中,除了select、from等关键字以外,其他大部分元素都可以理解为expression,比如:
代码语言:javascript复制select a,b from testdata2 where a>2
这里的 a,b,>,2都是expression
Expression的canonicalized操作
这个操作返回经过规范化处理后的表达式
规范化处理会在确保输出结果相同的前提下通过一些规则对表达式进重写
这个规范化有啥用呢?
举个例子:
代码语言:javascript复制select b,B,sum(A b) as ab,sum(B a) as ba from testdata2
where b>3 group by b
上面的代码中,b和B,sum(A b)和sum(B a) 虽然在外观上不一样,但实际计算的结果是完全一样的。而规范化操作会把b,B 和 sum(A b)和sum(B a)在外观上统一,这样可以使它们引用同一个实际计算的结果,避免多次计算。
这个规范化具体是怎么操作的呢?
借助了工具类:Canonicalize
核心方法execute
依据一些规则重写表达式,消除外观差异
代码语言:javascript复制def execute(e: Expression): Expression = {
expressionReorder(ignoreNamesTypes(e))
}
规范化结果集中的命名
两种情况:
- 对于AttributeReference引用类的表达式,主要做法是消除名称和可空性带来的差异
- GetStructField复杂类型的表达式,消除名称带来的差异
对于引用类型的表达式,判断是否相同,只需要引用的id(exprId)是相同的就ok,所以这里的处理方法是把name统一置none,来消除差异,比如:
代码语言:javascript复制select b,B,sum(A b) as ab,sum(B a) as ba from testdata2 where b>3 group by b
//name#exprId
Expression(b) ---> b#3
ignoreNamesTypes(b)----none#3
Expression(B) ---> B#3
ignoreNamesTypes(B)----none#3
结过上面转化,b和B都为none#3
计算类表达式
- 对于交换和结合运算(Add和Multiply)的子运算,通过“hashCode”来对左右子节点排序
- 对于交换和结合运算(Or和And)的子运算,通过“hashCode”来对左右子节点排序,但要求表达式必须是确定性的
- EqualTo和EqualNullSafe通过“hashCode”来对左右子节点排序。
- 其他比较(greatethan,LessThan)由“hashCode”反转。
- in中的元素按`hashCode'重新排序
private def expressionReorder(e: Expression): Expression = e match {
// 加法和乘法可以交换顺序,转换为seq,用Seq的sortBy进行排序
case a: Add => orderCommutative(a, { case Add(l, r) => Seq(l, r) }).reduce(Add)
case m: Multiply => orderCommutative(m, { case Multiply(l, r) => Seq(l, r) }).reduce(Multiply)
// or和and可以交换顺序,转换为seq,用Seq的sortBy进行排序,但要求必须是确定性的
case o: Or =>
orderCommutative(o, { case Or(l, r) if l.deterministic && r.deterministic => Seq(l, r) })
.reduce(Or)
case a: And =>
orderCommutative(a, { case And(l, r) if l.deterministic && r.deterministic => Seq(l, r)})
.reduce(And)
// EqualTo和EqualNullSafe通过“hashCode”来对左右子节点排序
case EqualTo(l, r) if l.hashCode() > r.hashCode() => EqualTo(r, l)
case EqualNullSafe(l, r) if l.hashCode() > r.hashCode() => EqualNullSafe(r, l)
// 其他比较greatethan,LessThan由“hashCode”反转,比如 在>时,如果l.hashCode() > r.hashCode(),就转为 LessThan(r, l)
case GreaterThan(l, r) if l.hashCode() > r.hashCode() => LessThan(r, l)
case LessThan(l, r) if l.hashCode() > r.hashCode() => GreaterThan(r, l)
case GreaterThanOrEqual(l, r) if l.hashCode() > r.hashCode() => LessThanOrEqual(r, l)
case LessThanOrEqual(l, r) if l.hashCode() > r.hashCode() => GreaterThanOrEqual(r, l)
// Note in the following `NOT` cases, `l.hashCode() <= r.hashCode()` holds. The reason is that
// canonicalization is conducted bottom-up -- see [[Expression.canonicalized]].
case Not(GreaterThan(l, r)) => LessThanOrEqual(l, r)
case Not(LessThan(l, r)) => GreaterThanOrEqual(l, r)
case Not(GreaterThanOrEqual(l, r)) => LessThan(l, r)
case Not(LessThanOrEqual(l, r)) => GreaterThan(l, r)
// in按`hashCode'重新排序
case In(value, list) if list.length > 1 => In(value, list.sortBy(_.hashCode()))
case _ => e
}
}
扩展操作semanticEquals
代码语言:javascript复制 // 两个表达式计算相同的结果时返回true,判断依据是:两个表达式都确定性的,
// 且两个表达式规范化之后相同
def semanticEquals(other: Expression): Boolean =
deterministic && other.deterministic && canonicalized == other.canonicalized
Hey!
我是小萝卜算子