背景
在使用calcite的时候,会遇到莫名奇妙的时候,sql在经过RBO优化后整个sql变形了,出现了如下内容
1 | FROM (VALUES (NULL, NULL, NULL, NULL, NULL, NULL)) AS `t` ( `DATE_CD`, `IDX_VAL`, `IDX_VALUE`, `test`, `IDX_ID`) |
原因
明确出现该场景的主要原因是sql出现了false的过滤条件,经过优化的时候calcite将该场景转为VALUES
案发现场
sql
1 |
|
1 | select DATE_CD from tempTable where 1=2 |
场景一
在RBO中使用CoreRules.FILTER_REDUCE_EXPRESSIONS
直接看源码
1 | public void onMatch(RelOptRuleCall call) { |
分析下具体的内容
这段代码会将获取到的所有condition经过下面的方法进行reduce,赋值给newConditionExp
1 | reduceExpressions(filter, expList, predicates, true, |
如果newConditionExp一直是true,就会直接prune这个filter,将input暴露出去1
2call.transformTo(
filter.getInput());
如果有reduce就会将新的filter替换旧的filter
1 | call.transformTo(call.builder() |
如果reduce后是只剩下一个数值或者null或者false,就会调用1
call.transformTo(createEmptyRelOrEquivalent(call, filter));
createEmptyRelOrEquivalent方法的内容很简单,就是构建上面的values
1 | /** |
解决方法
重写一个rule,将出现false就调用createEmptyRelOrEquivalent的地方去掉
1 | public void onMatch(RelOptRuleCall call) { |
返回的sql会变成
1 | SELECT `DATE_CD` |
场景二
自定义Rule中使用RelBuilder.filter方法
有时候我们会自己使用RelBuilder来构建sql,进行RBO。一旦我们filter塞进去两个对立的条件,就会出现empty的调用。
RelBuilder源码如下
1 | /** |
进一步看下 simplifier.simplifyFilterPredicates.
Rimplifier1
2
3
4
5
6
7
8
9
10
11
12
13
14public RexNode simplifyFilterPredicates(Iterable<? extends RexNode> predicates) {
final RexNode simplifiedAnds =
withPredicateElimination(Bug.CALCITE_2401_FIXED)
.simplifyUnknownAsFalse(
RexUtil.composeConjunction(rexBuilder, predicates));
if (simplifiedAnds.isAlwaysFalse()) {
return null;
}
// Remove cast of BOOLEAN NOT NULL to BOOLEAN or vice versa. Filter accepts
// nullable and not-nullable conditions, but a CAST might get in the way of
// other rewrites.
return removeNullabilityCast(simplifiedAnds);
}
进行条件的合并是在这个方法
1 | RexUtil.composeConjunction(rexBuilder, predicates) |
从Rimplifier.simplifyand 可以看出,从composeConjunction得到false的结果会直接返回null,返回的null会触发RelBuilder调用empty.
解决方法
重写RelBuilder,直接调用RexUtil.composeConjunction(cluster.getRexBuilder(), predicates)即可。
1 | /** |