blob: 91b284cf842ef64abfd3a8ba65d767c9def298aa [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.planner.index;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.Sets;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.RelFieldCollation.NullDirection;
import org.apache.drill.common.expression.CastExpression;
import org.apache.drill.common.expression.LogicalExpression;
import org.apache.drill.common.expression.SchemaPath;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class DrillIndexDefinition implements IndexDefinition {
/**
* The indexColumns is the list of column(s) on which this index is created. If there is more than 1 column,
* the order of the columns is important: index on {a, b} is not the same as index on {b, a}
* NOTE: the indexed column could be of type columnfamily.column
*/
@JsonProperty
protected final List<LogicalExpression> indexColumns;
/**
* nonIndexColumns: the list of columns that are included in the index as 'covering'
* columns but are not themselves indexed. These are useful for covering indexes where the
* query request can be satisfied directly by the index and avoid accessing the table altogether.
*/
@JsonProperty
protected final List<LogicalExpression> nonIndexColumns;
@JsonIgnore
protected final Set<LogicalExpression> allIndexColumns;
@JsonProperty
protected final List<LogicalExpression> rowKeyColumns;
@JsonProperty
protected final CollationContext indexCollationContext;
/**
* indexName: name of the index that should be unique within the scope of a table
*/
@JsonProperty
protected final String indexName;
protected final String tableName;
@JsonProperty
protected final IndexDescriptor.IndexType indexType;
@JsonProperty
protected final NullDirection nullsDirection;
public DrillIndexDefinition(List<LogicalExpression> indexCols,
CollationContext indexCollationContext,
List<LogicalExpression> nonIndexCols,
List<LogicalExpression> rowKeyColumns,
String indexName,
String tableName,
IndexType type,
NullDirection nullsDirection) {
this.indexColumns = indexCols;
this.nonIndexColumns = nonIndexCols;
this.rowKeyColumns = rowKeyColumns;
this.indexName = indexName;
this.tableName = tableName;
this.indexType = type;
this.allIndexColumns = Sets.newHashSet(indexColumns);
this.allIndexColumns.addAll(nonIndexColumns);
this.indexCollationContext = indexCollationContext;
this.nullsDirection = nullsDirection;
}
@Override
public int getIndexColumnOrdinal(LogicalExpression path) {
int id = indexColumns.indexOf(path);
return id;
}
@Override
public boolean isCoveringIndex(List<LogicalExpression> columns) {
return allIndexColumns.containsAll(columns);
}
@Override
public boolean allColumnsIndexed(Collection<LogicalExpression> columns) {
return columnsInIndexFields(columns, indexColumns);
}
@Override
public boolean someColumnsIndexed(Collection<LogicalExpression> columns) {
return someColumnsInIndexFields(columns, indexColumns);
}
public boolean pathExactIn(SchemaPath path, Collection<LogicalExpression> exprs) {
for (LogicalExpression expr : exprs) {
if (expr instanceof SchemaPath) {
if (((SchemaPath) expr).toExpr().equals(path.toExpr())) {
return true;
}
}
}
return false;
}
boolean castIsCompatible(CastExpression castExpr, Collection<LogicalExpression> indexFields) {
for (LogicalExpression indexExpr : indexFields) {
if (indexExpr.getClass() != castExpr.getClass()) {
continue;
}
CastExpression indexCastExpr = (CastExpression)indexExpr;
// we compare input using equals because we know we are comparing SchemaPath,
// if we extend to support other expression, make sure the equals of that expression
// is implemented properly, otherwise it will fall to identity comparison
if (!castExpr.getInput().equals(indexCastExpr.getInput()) ) {
continue;
}
if (castExpr.getMajorType().getMinorType() != indexCastExpr.getMajorType().getMinorType()) {
continue;
}
return true;
}
return false;
}
protected boolean columnsInIndexFields(Collection<LogicalExpression> columns, Collection<LogicalExpression> indexFields) {
// we need to do extra check, so we could allow the case when query condition expression is not identical with indexed fields
// and they still could use the index either by implicit cast or the difference is allowed, e.g. width of varchar
for (LogicalExpression col : columns) {
if (col instanceof CastExpression) {
if (!castIsCompatible((CastExpression) col, indexFields)) {
return false;
}
}
else {
if (!pathExactIn((SchemaPath)col, indexFields)) {
return false;
}
}
}
return true;//indexFields.containsAll(columns);
}
protected boolean someColumnsInIndexFields(Collection<LogicalExpression> columns,
Collection<LogicalExpression> indexFields) {
// we need to do extra check, so we could allow the case when query condition expression is not identical with indexed fields
// and they still could use the index either by implicit cast or the difference is allowed, e.g. width of varchar
for (LogicalExpression col : columns) {
if (col instanceof CastExpression) {
if (castIsCompatible((CastExpression) col, indexFields)) {
return true;
}
}
else {
if (pathExactIn((SchemaPath)col, indexFields)) {
return true;
}
}
}
return false;
}
@Override
public String toString() {
String columnsDesc = " Index columns: " + indexColumns.toString() + " Non-Index columns: " + nonIndexColumns.toString();
String desc = "Table: " + tableName + " Index: " + indexName + columnsDesc;
return desc;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
DrillIndexDefinition index1 = (DrillIndexDefinition) o;
return tableName.equals(index1.tableName)
&& indexName.equals(index1.indexName)
&& indexType.equals(index1.indexType)
&& indexColumns.equals(index1.indexColumns);
}
@Override
public int hashCode() {
final int prime = 31;
final String fullName = tableName + indexName;
int result = 1;
result = prime * result + fullName.hashCode();
result = prime * result + indexType.hashCode();
return result;
}
@Override
@JsonProperty
public String getIndexName() {
return indexName;
}
@Override
public String getTableName() {
return tableName;
}
@Override
@JsonProperty
public IndexDescriptor.IndexType getIndexType() {
return indexType;
}
@Override
@JsonProperty
public List<LogicalExpression> getRowKeyColumns() {
return this.rowKeyColumns;
}
@Override
@JsonProperty
public List<LogicalExpression> getIndexColumns() {
return this.indexColumns;
}
@Override
@JsonProperty
public List<LogicalExpression> getNonIndexColumns() {
return this.nonIndexColumns;
}
@Override
@JsonIgnore
public RelCollation getCollation() {
if (indexCollationContext != null) {
return RelCollations.of(indexCollationContext.relFieldCollations);
}
return null;
}
@Override
@JsonIgnore
public Map<LogicalExpression, RelFieldCollation> getCollationMap() {
return indexCollationContext.collationMap;
}
@Override
@JsonIgnore
public NullDirection getNullsOrderingDirection() {
return nullsDirection;
}
}