blob: 635c766f0ffce55831968e9756a992fdd76845e5 [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.
*/
package org.apache.drill.exec.store.base.filter;
import java.util.List;
import org.apache.calcite.rex.RexNode;
import org.apache.drill.common.PlanStringBuilder;
import org.apache.drill.common.types.TypeProtos.MinorType;
import com.google.common.base.Preconditions;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
/**
* Condensed form of a Drill WHERE clause expression
* node. Models only those nodes that are typically used
* in rewrite-style filter push-down. Not intended for
* the more complex interpreted form of filter push-down
* such as that needed for directory-based partitions.
* <p>
* Each node references the source Calcite node as well as
* the selectivity that Calcite attaches to the node. Any
* expressions pushed to a scan must reduce the scan
* cost by the amount of the selectivity, else Calcite will
* conclude that the original plan (without push-down) is
* cheaper.
*/
public abstract class ExprNode {
@JsonIgnore
private RexNode rexNode;
public ExprNode() {
rexNode = null;
}
public void tag(RexNode rexNode) {
this.rexNode = rexNode;
}
public RexNode rexNode() { return rexNode; }
public abstract double selectivity();
public interface ColumnTypeNode {
String colName();
MinorType type();
}
/**
* An expression node with an unlimited set of children.
*/
public abstract static class ListNode extends ExprNode {
@JsonProperty("children")
public final List<ExprNode> children;
public ListNode(List<ExprNode> children) {
this.children = children;
}
}
/**
* Represents a set of AND'ed expressions in Conjunctive Normal
* Form (CNF). Typically the WHERE clause is rewritten to gather
* all expressions into a single CNF node. The children are often
* called "conjuncts."
*/
public static class AndNode extends ListNode {
@JsonCreator
public AndNode(@JsonProperty("children") List<ExprNode> children) {
super(children);
}
@Override
public double selectivity() {
double selectivity = 1;
for (ExprNode child : children) {
selectivity *= child.selectivity();
}
return selectivity;
}
}
/**
* Represents a set of OR'ed expressions in Disjunctive Normal
* Form (CNF). Or'ed expresssions often cannot be pushed down except in the
* special case of a series of expressions on the same column, which can
* sometimes be handled as a series of requests, each for a different disjunct.
*
*/
public static class OrNode extends ListNode {
@JsonCreator
public OrNode(@JsonProperty("children") List<ExprNode> children) {
super(children);
}
/**
* Compute the selectivity of this DNF (OR) clause. Drill assumes
* the selectivity of = is 0.15. An OR is a series of equal statements,
* so selectivity is n * 0.15. However, limit total selectivity to
* 0.9 (that is, if there are more than 6 clauses in the DNF, assume
* at least some reduction.)
*
* @return the estimated selectivity of this DNF clause
*/
@Override
public double selectivity() {
if (children.size() == 0) {
return 1.0;
}
double selectivity = 0;
for (ExprNode child : children) {
selectivity += child.selectivity();
}
return Math.min(0.9, selectivity);
}
}
public abstract static class RelOpNode extends ExprNode {
@JsonProperty("op")
public final RelOp op;
public RelOpNode(RelOp op) {
this.op = op;
}
@Override
public double selectivity() {
return op.selectivity();
}
}
/**
* Semanticized form of a Calcite relational operator. Abstracts
* out the Drill implementation details to capture just the
* column name, operator and value. Supports only expressions
* of the form:<br>
* {@code <column> <relop> <const>}<br>
* Where the column is a simple name (not an array or map reference),
* the relop is one of a defined set, and the constant is one
* of the defined Drill types.
* <p>
* (The driver will convert expressions of the form:<br>
* {@code <const> <relop> <column>}<br>
* into the normalized form represented here.
*/
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({"colName", "op", "value"})
public static class ColRelOpConstNode extends RelOpNode {
@JsonProperty("colName")
public final String colName;
@JsonProperty("value")
public final ConstantHolder value;
@JsonCreator
public ColRelOpConstNode(
@JsonProperty("colName") String colName,
@JsonProperty("op") RelOp op,
@JsonProperty("value") ConstantHolder value) {
super(op);
Preconditions.checkArgument(op.argCount() == 1 || value != null);
this.colName = colName;
this.value = value;
}
/**
* Rewrite the RelOp with a normalized value.
*
* @param from the original RelOp
* @param value the new value with a different type and matching
* value
*/
public ColRelOpConstNode(ColRelOpConstNode from, ConstantHolder value) {
super(from.op);
Preconditions.checkArgument(from.op.argCount() == 2);
this.colName = from.colName;
this.value = value;
}
/**
* Rewrite a relop using the given normalized value.
*
* @param normalizedValue given normalized value
* @return a new RelOp with the normalized value. Will be the same relop
* if the normalized value is the same as the unnormalized value.
*/
public ColRelOpConstNode normalize(ConstantHolder normalizedValue) {
if (value == normalizedValue) {
return this;
}
return new ColRelOpConstNode(this, normalizedValue);
}
public ColRelOpConstNode rewrite(String newName, ConstantHolder newValue) {
if (value == newValue && colName.equals(newName)) {
return this;
}
return new ColRelOpConstNode(newName, op, newValue);
}
@Override
public String toString() {
PlanStringBuilder builder = new PlanStringBuilder(this)
.field("op", op.name())
.field("colName", colName);
if (value != null) {
builder.field("type", value.type.name())
.field("value", value.value);
}
return builder.toString();
}
}
}