/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.sql.optimizer.plan;
import com.foundationdb.ais.model.Index;
import com.foundationdb.ais.model.IndexColumn;
import com.foundationdb.ais.model.Table;
import com.foundationdb.server.types.texpressions.Comparison;
import com.foundationdb.sql.optimizer.plan.ConditionsCount.HowMany;
import com.foundationdb.sql.optimizer.plan.Sort.OrderByExpression;
import com.foundationdb.sql.optimizer.rule.TypeResolver;
import com.foundationdb.sql.optimizer.rule.PlanContext;
import com.foundationdb.sql.optimizer.rule.ConstantFolder.Folder;
import com.foundationdb.sql.optimizer.rule.range.ColumnRanges;
import java.util.ArrayList;
import java.util.List;
public final class SingleIndexScan extends IndexScan implements EqualityColumnsScan {
private Index index;
private ColumnRanges conditionRange;
// First equalities in the order of the index.
private List<ExpressionNode> equalityComparands;
// This is how the indexed result will be ordered from using this index.
private List<OrderByExpression> ordering;
private OrderEffectiveness orderEffectiveness;
private boolean usesAllColumns;
// Conditions subsumed by this index.
// TODO: any cases where a condition is only partially handled and
// still needs to be checked with Select?
private List<ConditionExpression> conditions;
// Columns in order, should the index be used as covering.
private List<ExpressionNode> columns;
// Followed by an optional inequality.
private ExpressionNode lowComparand, highComparand;
// TODO: This doesn't work for merging: consider x < ? AND x <= ?.
// May need building of index keys in the expressions subsystem.
private boolean lowInclusive, highInclusive;
private PlanContext context;
private boolean includeUnionsAsEquality = true;
public SingleIndexScan(Index index, TableSource table, PlanContext context)
{
super(table);
this.index = index;
this.context = context;
}
public SingleIndexScan(Index index,
TableSource rootMostTable,
TableSource rootMostInnerTable,
TableSource leafMostInnerTable,
TableSource leafMostTable,
PlanContext context)
{
super(rootMostTable, rootMostInnerTable, leafMostInnerTable, leafMostTable);
this.index = index;
this.context = context;
}
public Index getIndex() {
return index;
}
@Override
public List<ExpressionNode> getColumns() {
return columns;
}
public void setColumns(List<ExpressionNode> columns) {
this.columns = columns;
}
public ColumnRanges getConditionRange() {
return conditionRange;
}
public void addRangeCondition(ColumnRanges range) {
assert conditionRange == null : conditionRange;
conditionRange = range;
internalGetConditions().addAll(range.getConditions());
}
public List<ExpressionNode> getEqualityComparands() {
return equalityComparands;
}
public void addEqualityCondition(ConditionExpression condition,
ExpressionNode comparand) {
if (equalityComparands == null)
equalityComparands = new ArrayList<>();
equalityComparands.add(comparand);
internalGetConditions().add(condition);
}
public void addInequalityCondition(ConditionExpression condition,
Comparison comparison,
ExpressionNode comparand) {
if ((comparison == Comparison.GT) || (comparison == Comparison.GE)) {
if (lowComparand == null) {
lowComparand = comparand;
lowInclusive = (comparison == Comparison.GE);
}
else if (lowInclusive == (comparison == Comparison.GE)) {
List<ExpressionNode> operands = new ArrayList<>(2);
operands.add(lowComparand);
operands.add(comparand);
lowComparand = new FunctionExpression("_max",
operands,
lowComparand.getSQLtype(),
null,
lowComparand.getType());
setPreptimeValue (lowComparand);
}
else
// TODO: Could do the MAX anyway and test the conditions later.
// Might take some refactoring to know which
// conditions are already there.
return;
}
else if ((comparison == Comparison.LT) || (comparison == Comparison.LE)) {
if (highComparand == null) {
highComparand = comparand;
highInclusive = (comparison == Comparison.LE);
}
else if (highInclusive == (comparison == Comparison.LE)) {
List<ExpressionNode> operands = new ArrayList<>(2);
operands.add(highComparand);
operands.add(comparand);
highComparand = new FunctionExpression("_min",
operands,
highComparand.getSQLtype(),
null,
highComparand.getType());
setPreptimeValue (highComparand);
}
else
// Not really an inequality.
return;
}
else {
return;
}
internalGetConditions().add(condition);
}
private void setPreptimeValue (ExpressionNode expression) {
TypeResolver.ResolvingVisitor visitor =
new TypeResolver.ResolvingVisitor(context, new Folder(context));
visitor.visit(expression);
}
@Override
public ExpressionNode getLowComparand() {
return lowComparand;
}
@Override
public boolean isLowInclusive() {
return lowInclusive;
}
@Override
public ExpressionNode getHighComparand() {
return highComparand;
}
@Override
public boolean isHighInclusive() {
return highInclusive;
}
public void setLowComparand(ExpressionNode comparand, boolean inclusive) {
lowComparand = comparand;
lowInclusive = inclusive;
}
public void setHighComparand(ExpressionNode comparand, boolean inclusive) {
highComparand = comparand;
highInclusive = inclusive;
}
@Override
protected void deepCopy(DuplicateMap map) {
super.deepCopy(map);
if (lowComparand != null)
lowComparand = (ConditionExpression)lowComparand.duplicate(map);
if (highComparand != null)
highComparand = (ConditionExpression)highComparand.duplicate(map);
equalityComparands = duplicateList(equalityComparands, map);
ordering = duplicateList(ordering, map);
}
@Override
public List<OrderByExpression> getOrdering() {
return ordering;
}
public void setOrdering(List<OrderByExpression> ordering) {
this.ordering = ordering;
}
public List<ConditionExpression> getConditions() {
return conditions;
}
public boolean hasConditions() {
return ((conditions != null) && !conditions.isEmpty());
}
@Override
public OrderEffectiveness getOrderEffectiveness() {
return orderEffectiveness;
}
public void setOrderEffectiveness(OrderEffectiveness orderEffectiveness) {
this.orderEffectiveness = orderEffectiveness;
}
@Override
public List<IndexColumn> getIndexColumns() {
return index.getAllColumns();
}
@Override
public int getNKeyColumns() {
return index.getKeyColumns().size();
}
@Override
public boolean usesAllColumns() {
return usesAllColumns;
}
@Override
public void setUsesAllColumns(boolean usesAllColumns) {
this.usesAllColumns = usesAllColumns;
}
@Override
protected String summarizeIndex(int indentation) {
return String.valueOf(index);
}
@Override
public boolean isAscendingAt(int i) {
if (index.isSpatial()) {
int firstSpatialColumn = index.firstSpatialArgument();
if (i == firstSpatialColumn)
return true;
if (i > firstSpatialColumn)
i += index.dimensions() - 1;
}
return index.getAllColumns().get(i).isAscending();
}
@Override
public boolean isRecoverableAt(int i) {
if (index.isSpatial()) {
int firstSpatialColumn = index.firstSpatialArgument();
if (i == firstSpatialColumn)
return false;
if (i > firstSpatialColumn)
i += index.dimensions() - 1;
}
return index.getAllColumns().get(i).isRecoverable();
}
@Override
public Table getLeafMostAisTable() {
return index.leafMostTable();
}
@Override
public List<IndexColumn> getAllColumns() {
return index.getAllColumns();
}
@Override
public int getNEquality() {
int nequals = 0;
if (equalityComparands != null)
nequals = equalityComparands.size();
if (includeUnionsAsEquality) {
nequals += getNUnions();
}
return nequals;
}
@Override
public int getNUnions() {
if ((conditionRange != null) && conditionRange.isAllSingle())
return 1;
else
return 0;
}
@Override
public void setIncludeUnionAsEquality(boolean unionIsEquality) {
this.includeUnionsAsEquality = unionIsEquality;
}
@Override
public void incrementConditionsCounter(ConditionsCounter<? super ConditionExpression> counter) {
for (ConditionExpression cond : getConditions())
counter.increment(cond);
}
@Override
public boolean isUseful(ConditionsCount<? super ConditionExpression> count) {
for (ConditionExpression cond : getConditions()) {
if (count.getCount(cond) == HowMany.ONE)
return true;
}
return false;
}
@Override
public Table findCommonAncestor(IndexScan otherScan) {
TableSource myTable = getLeafMostTable();
TableSource otherTable = otherScan.getLeafMostTable();
int myDepth = myTable.getTable().getDepth();
int otherDepth = otherTable.getTable().getDepth();
while (myDepth > otherDepth) {
myTable = myTable.getParentTable();
myDepth = myTable.getTable().getDepth();
}
while (otherDepth > myDepth) {
otherTable = otherTable.getParentTable();
otherDepth = otherTable.getTable().getDepth();
}
while (myTable != otherTable) {
myTable = myTable.getParentTable();
otherTable = otherTable.getParentTable();
}
return myTable.getTable().getTable();
}
@Override
protected void describeConditionRange(StringBuilder output) {
if (conditionRange != null) {
output.append(", UNIONs of ");
output.append(conditionRange.describeRanges());
}
}
@Override
protected void describeEqualityComparands(StringBuilder output) {
if (equalityComparands != null) {
for (ExpressionNode expression : equalityComparands) {
output.append(", =");
output.append(expression);
}
}
}
private List<ConditionExpression> internalGetConditions() {
if (conditions == null)
conditions = new ArrayList<>();
return conditions;
}
@Override
public void visitComparands(ExpressionRewriteVisitor v) {
if (equalityComparands != null) {
for (int i = 0; i < equalityComparands.size(); i++) {
if (equalityComparands.get(i) != null)
equalityComparands.set(i, equalityComparands.get(i).accept(v));
}
}
if (lowComparand != null)
lowComparand = lowComparand.accept(v);
if (highComparand != null)
highComparand = highComparand.accept(v);
}
@Override
public void visitComparands(ExpressionVisitor v) {
if (equalityComparands != null) {
for (ExpressionNode comparand : equalityComparands) {
if (comparand != null)
comparand.accept(v);
}
}
if (lowComparand != null)
lowComparand.accept(v);
if (highComparand != null)
highComparand.accept(v);
}
}