com.hazelcast.org.apache.calcite.rex.LogicVisitor Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.org.apache.calcite.rex;
import com.hazelcast.org.apache.calcite.plan.RelOptUtil.Logic;
import com.hazelcast.com.google.common.collect.Iterables;
import com.hazelcast.org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import static java.util.Objects.requireNonNull;
/**
* Visitor pattern for traversing a tree of {@link RexNode} objects.
*/
public class LogicVisitor extends RexUnaryBiVisitor<@Nullable Logic> {
private final RexNode seek;
private final Collection logicCollection;
/** Creates a LogicVisitor. */
private LogicVisitor(RexNode seek, Collection logicCollection) {
super(true);
this.seek = seek;
this.logicCollection = logicCollection;
}
/** Finds a suitable logic for evaluating {@code seek} within a list of
* expressions.
*
* Chooses a logic that is safe (that is, gives the right
* answer) with the fewest possibilities (that is, we prefer one that
* returns [true as true, false as false, unknown as false] over one that
* distinguishes false from unknown).
*/
public static Logic find(Logic logic, List nodes,
RexNode seek) {
final Set set = EnumSet.noneOf(Logic.class);
final LogicVisitor visitor = new LogicVisitor(seek, set);
for (RexNode node : nodes) {
node.accept(visitor, logic);
}
// Convert FALSE (which can only exist within LogicVisitor) to
// UNKNOWN_AS_TRUE.
if (set.remove(Logic.FALSE)) {
set.add(Logic.UNKNOWN_AS_TRUE);
}
switch (set.size()) {
case 0:
throw new IllegalArgumentException("not found: " + seek);
case 1:
return Iterables.getOnlyElement(set);
default:
return Logic.TRUE_FALSE_UNKNOWN;
}
}
public static void collect(RexNode node, RexNode seek, Logic logic,
List logicList) {
node.accept(new LogicVisitor(seek, logicList), logic);
// Convert FALSE (which can only exist within LogicVisitor) to
// UNKNOWN_AS_TRUE.
Collections.replaceAll(logicList, Logic.FALSE, Logic.UNKNOWN_AS_TRUE);
}
@Override public @Nullable Logic visitCall(RexCall call, @Nullable Logic logic) {
final Logic arg0 = logic;
switch (call.getKind()) {
case IS_NOT_NULL:
case IS_NULL:
logic = Logic.TRUE_FALSE_UNKNOWN;
break;
case IS_TRUE:
case IS_NOT_TRUE:
logic = Logic.UNKNOWN_AS_FALSE;
break;
case IS_FALSE:
case IS_NOT_FALSE:
logic = Logic.UNKNOWN_AS_TRUE;
break;
case NOT:
logic = requireNonNull(logic, "logic").negate2();
break;
case CASE:
logic = Logic.TRUE_FALSE_UNKNOWN;
break;
default:
break;
}
switch (requireNonNull(logic, "logic")) {
case TRUE:
switch (call.getKind()) {
case AND:
break;
default:
logic = Logic.TRUE_FALSE_UNKNOWN;
}
break;
default:
break;
}
for (RexNode operand : call.operands) {
operand.accept(this, logic);
}
return end(call, arg0);
}
@Override protected @Nullable Logic end(RexNode node, @Nullable Logic arg) {
if (node.equals(seek)) {
logicCollection.add(requireNonNull(arg, "arg"));
}
return arg;
}
@Override public @Nullable Logic visitOver(RexOver over, @Nullable Logic arg) {
return end(over, arg);
}
@Override public @Nullable Logic visitFieldAccess(RexFieldAccess fieldAccess,
@Nullable Logic arg) {
return end(fieldAccess, arg);
}
@Override public @Nullable Logic visitSubQuery(RexSubQuery subQuery, @Nullable Logic arg) {
if (!subQuery.getType().isNullable()) {
if (arg == Logic.TRUE_FALSE_UNKNOWN) {
arg = Logic.TRUE_FALSE;
}
}
return end(subQuery, arg);
}
}