blob: 2bfb98c235628f8bdf22a18f4aaf6dc961af4e45 [file] [log] [blame]
/*
* 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.
*/
/**
* 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 org.apache.drill.exec.planner.index;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexCorrelVariable;
import org.apache.calcite.rex.RexDynamicParam;
import org.apache.calcite.rex.RexFieldAccess;
import org.apache.calcite.rex.RexInputRef;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexLocalRef;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexOver;
import org.apache.calcite.rex.RexRangeRef;
import org.apache.calcite.rex.RexVisitorImpl;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* A visitor class that analyzes a filter condition (typically an index condition)
* and a supplied input collation and determines what the output collation would be
* after applying the filter.
*/
public class FindFiltersForCollation extends RexVisitorImpl<Boolean> {
// map of field collation (e.g [0]) to the list of filter conditions
// involving the same field
private Map<Integer, List<RexNode> > collationFilterMap = Maps.newHashMap();
// set of comparison operators that allow collation
private Set<SqlKind> allowedComparisons = Sets.newHashSet();
// input rel's rowtype
private RelDataType inputRowType;
private int currentFieldIndex;
public FindFiltersForCollation(RelNode input) {
super(true);
inputRowType = input.getRowType();
init();
}
/**
* Initialize the set of comparison operators that allow creating collation property.
*/
private void init() {
allowedComparisons.addAll(SqlKind.COMPARISON);
allowedComparisons.add(SqlKind.LIKE);
}
/**
* For each RelFieldCollation, gather the set of filter conditions corresponding to it
* e.g suppose input collation is [0][1] and there are filter conditions: $0 = 5 AND $1 > 10 AND $1 <20
* then the map will have 2 entries:
* [0] -> ($0 = 5)
* [1] -> {($1 > 10), ($1 < 20)}
*
* @param indexCondition index condition to analyze
* @return list of output RelFieldCollation
*/
public Map<Integer, List<RexNode> > analyze(RexNode indexCondition) {
for (int idx = 0; idx < inputRowType.getFieldCount(); idx++) {
currentFieldIndex = idx;
indexCondition.accept(this);
}
return collationFilterMap;
}
@Override
public Boolean visitInputRef(RexInputRef inputRef) {
if (inputRef.getIndex() == currentFieldIndex) {
return true;
}
return false;
}
@Override
public Boolean visitLiteral(RexLiteral literal) {
return true;
}
@Override
public Boolean visitOver(RexOver over) {
return false;
}
@Override
public Boolean visitCorrelVariable(RexCorrelVariable correlVariable) {
return false;
}
@Override
public Boolean visitCall(RexCall call) {
final SqlOperator op = call.getOperator();
final SqlKind kind = op.getKind();
if (kind == SqlKind.AND) {
for (RexNode n : call.getOperands()) {
n.accept(this);
}
} else if (kind == SqlKind.CAST) {
// For the filter analyzer itself, if the Project has not been pushed
// down below the Filter, then CAST is present in the filter condition.
// Return True for such case since CAST exprs are valid for collation.
// Otherwise, filter is only referencing output of the Project and we won't
// hit this else condition (i.e filter will have $0, $1 etc which would be
// visited by visitInputRef()).
return true;
} else if (op == SqlStdOperatorTable.ITEM) {
List<RexNode> ops = call.getOperands();
boolean left = ops.get(0).accept(this);
boolean right = ops.get(1).accept(this);
return left && right;
} else if (allowedComparisons.contains(kind)) {
List<RexNode> ops = call.getOperands();
boolean left = ops.get(0).accept(this);
boolean right = ops.get(1).accept(this);
if (left && right) {
if (collationFilterMap.containsKey(currentFieldIndex)) {
List<RexNode> n = collationFilterMap.get(currentFieldIndex);
n.add(call);
} else {
List<RexNode> clist = Lists.newArrayList();
clist.add(call);
collationFilterMap.put(currentFieldIndex, clist);
}
return true;
}
}
return false;
}
@Override
public Boolean visitDynamicParam(RexDynamicParam dynamicParam) {
return false;
}
@Override
public Boolean visitRangeRef(RexRangeRef rangeRef) {
return false;
}
@Override
public Boolean visitFieldAccess(RexFieldAccess fieldAccess) {
return false;
}
@Override
public Boolean visitLocalRef(RexLocalRef localRef) {
return false;
}
}