blob: 3943cf4b09be1184ae2d875b02a9b038fba6aafc [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.asterix.optimizer.rules.cbo;
import java.util.Map;
import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.algebricks.common.utils.Pair;
import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
import org.apache.hyracks.algebricks.core.algebra.base.OperatorAnnotations;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.AggregateOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.DelegateOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.DistinctOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.DistributeResultOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.EmptyTupleSourceOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.ExchangeOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.ForwardOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.GroupByOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.IndexInsertDeleteUpsertOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.InsertDeleteUpsertOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.IntersectOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterJoinOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestMapOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.LimitOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.MaterializeOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.NestedTupleSourceOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.ProjectOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.ReplicateOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.RunningAggregateOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.ScriptOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.SinkOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.SplitOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.SubplanOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.TokenizeOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnionAllOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestMapOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.WindowOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.WriteOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.WriteResultOperator;
import org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalOperatorVisitor;
/**
* A visitor that annotates an operator with its estimated cardinality and estimated cost.
*/
public class EstimatedCostComputationVisitor implements ILogicalOperatorVisitor<Pair<Double, Double>, Double> {
public EstimatedCostComputationVisitor() {
}
@Override
public Pair<Double, Double> visitAggregateOperator(AggregateOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitRunningAggregateOperator(RunningAggregateOperator op, Double arg)
throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitEmptyTupleSourceOperator(EmptyTupleSourceOperator op, Double arg)
throws AlgebricksException {
// Empty tuple source operator sends an empty tuple to downstream operators.
return new Pair<>(0.0, 0.0);
}
@Override
public Pair<Double, Double> visitGroupByOperator(GroupByOperator op, Double arg) throws AlgebricksException {
// Needs more work in the cardinality estimation code to estimate group by cardinality and cost.
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitLimitOperator(LimitOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitInnerJoinOperator(InnerJoinOperator op, Double arg) throws AlgebricksException {
return visitInnerJoin(op, arg);
}
@Override
public Pair<Double, Double> visitLeftOuterJoinOperator(LeftOuterJoinOperator op, Double arg)
throws AlgebricksException {
return visitLeftOuterUnnest(op, arg);
}
@Override
public Pair<Double, Double> visitNestedTupleSourceOperator(NestedTupleSourceOperator op, Double arg)
throws AlgebricksException {
Pair<Double, Double> cardCost = annotate(this, op, arg);
return op.getDataSourceReference().getValue().getOperatorTag() == LogicalOperatorTag.SUBPLAN ? cardCost
: new Pair<>(0.0, 0.0);
}
@Override
public Pair<Double, Double> visitOrderOperator(OrderOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitAssignOperator(AssignOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitWindowOperator(WindowOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitSelectOperator(SelectOperator op, Double arg) throws AlgebricksException {
Pair<Double, Double> cardCost = op.getInputs().get(0).getValue().accept(this, arg);
for (Map.Entry<String, Object> anno : op.getAnnotations().entrySet()) {
if (anno.getValue() != null && anno.getKey().equals(OperatorAnnotations.OP_OUTPUT_CARDINALITY)) {
cardCost.setFirst((Double) anno.getValue());
} else if (anno.getValue() != null && anno.getKey().equals(OperatorAnnotations.OP_COST_TOTAL)) {
cardCost.setSecond((Double) anno.getValue());
}
}
return cardCost;
}
@Override
public Pair<Double, Double> visitDelegateOperator(DelegateOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitProjectOperator(ProjectOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitReplicateOperator(ReplicateOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitSplitOperator(SplitOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitMaterializeOperator(MaterializeOperator op, Double arg)
throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitScriptOperator(ScriptOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitSubplanOperator(SubplanOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitSinkOperator(SinkOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitUnionOperator(UnionAllOperator op, Double arg) throws AlgebricksException {
// Needs more work.
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitUnnestOperator(UnnestOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitLeftOuterUnnestOperator(LeftOuterUnnestOperator op, Double arg)
throws AlgebricksException {
return visitLeftOuterUnnest(op, arg);
}
@Override
public Pair<Double, Double> visitUnnestMapOperator(UnnestMapOperator op, Double arg) throws AlgebricksException {
Pair<Double, Double> cardCost = new Pair<>(0.0, 0.0);
for (Map.Entry<String, Object> anno : op.getAnnotations().entrySet()) {
if (anno.getValue() != null && anno.getKey().equals(OperatorAnnotations.OP_OUTPUT_CARDINALITY)) {
cardCost.setFirst((Double) anno.getValue());
} else if (anno.getValue() != null && anno.getKey().equals(OperatorAnnotations.OP_COST_TOTAL)) {
cardCost.setSecond((Double) anno.getValue());
}
}
return cardCost;
}
@Override
public Pair<Double, Double> visitLeftOuterUnnestMapOperator(LeftOuterUnnestMapOperator op, Double arg)
throws AlgebricksException {
return visitLeftOuterUnnest(op, arg);
}
@Override
public Pair<Double, Double> visitDataScanOperator(DataSourceScanOperator op, Double arg)
throws AlgebricksException {
Pair<Double, Double> cardCost = new Pair<>(0.0, 0.0);
for (Map.Entry<String, Object> anno : op.getAnnotations().entrySet()) {
if (anno.getValue() != null && anno.getKey().equals(OperatorAnnotations.OP_INPUT_CARDINALITY)) {
cardCost.setFirst((Double) anno.getValue());
} else if (anno.getValue() != null && anno.getKey().equals(OperatorAnnotations.OP_COST_TOTAL)) {
cardCost.setSecond((Double) anno.getValue());
}
}
return cardCost;
}
@Override
public Pair<Double, Double> visitDistinctOperator(DistinctOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitExchangeOperator(ExchangeOperator op, Double arg) throws AlgebricksException {
double exchCost = 0;
if (arg != null) {
exchCost = arg;
}
Pair<Double, Double> cardCost = op.getInputs().get(0).getValue().accept(this, arg);
if (exchCost != 0) {
op.getAnnotations().put(OperatorAnnotations.OP_COST_LOCAL, exchCost);
op.getAnnotations().put(OperatorAnnotations.OP_COST_TOTAL, exchCost + cardCost.getSecond());
} else {
op.getAnnotations().put(OperatorAnnotations.OP_COST_LOCAL, 0.0);
op.getAnnotations().put(OperatorAnnotations.OP_COST_TOTAL, cardCost.getSecond());
}
op.getAnnotations().put(OperatorAnnotations.OP_OUTPUT_CARDINALITY, cardCost.getFirst());
return cardCost;
}
@Override
public Pair<Double, Double> visitWriteOperator(WriteOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitDistributeResultOperator(DistributeResultOperator op, Double arg)
throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitWriteResultOperator(WriteResultOperator op, Double arg)
throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitInsertDeleteUpsertOperator(InsertDeleteUpsertOperator op, Double arg)
throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitIndexInsertDeleteUpsertOperator(IndexInsertDeleteUpsertOperator op, Double arg)
throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitTokenizeOperator(TokenizeOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitForwardOperator(ForwardOperator op, Double arg) throws AlgebricksException {
return annotate(this, op, arg);
}
@Override
public Pair<Double, Double> visitIntersectOperator(IntersectOperator op, Double arg) throws AlgebricksException {
// Needs more work.
return annotate(this, op, arg);
}
private static Pair<Double, Double> annotate(EstimatedCostComputationVisitor visitor, ILogicalOperator op,
Double arg) throws AlgebricksException {
if (op.getInputs().isEmpty()) {
return new Pair<>(0.0, 0.0);
}
Pair<Double, Double> cardCost = op.getInputs().get(0).getValue().accept(visitor, arg);
op.getAnnotations().put(OperatorAnnotations.OP_OUTPUT_CARDINALITY, cardCost.getFirst());
op.getAnnotations().put(OperatorAnnotations.OP_COST_TOTAL, cardCost.getSecond());
op.getAnnotations().put(OperatorAnnotations.OP_COST_LOCAL, 0.0);
return cardCost;
}
// Visits an operator that has the left outer semantics, e.g.,
// left outer join, left outer unnest, left outer unnest map.
private Pair<Double, Double> visitLeftOuterUnnest(ILogicalOperator operator, Double arg)
throws AlgebricksException {
// Needs more work.
return operator.getInputs().get(0).getValue().accept(this, arg);
}
// Visits an inner join operator.
private Pair<Double, Double> visitInnerJoin(InnerJoinOperator joinOperator, Double arg) throws AlgebricksException {
Pair<Double, Double> cardCost = new Pair<>(0.0, 0.0);
ILogicalOperator left = joinOperator.getInputs().get(0).getValue();
ILogicalOperator right = joinOperator.getInputs().get(1).getValue();
double leftExchangeCost = 0;
double rightExchangeCost = 0;
for (Map.Entry<String, Object> anno : joinOperator.getAnnotations().entrySet()) {
if (anno.getValue() != null && anno.getKey().equals(OperatorAnnotations.OP_OUTPUT_CARDINALITY)) {
cardCost.setFirst((Double) anno.getValue());
} else if (anno.getValue() != null && anno.getKey().equals(OperatorAnnotations.OP_COST_TOTAL)) {
cardCost.setSecond((Double) anno.getValue());
} else if (anno.getValue() != null && anno.getKey().equals(OperatorAnnotations.OP_LEFT_EXCHANGE_COST)) {
leftExchangeCost = (double) anno.getValue();
} else if (anno.getValue() != null && anno.getKey().equals(OperatorAnnotations.OP_RIGHT_EXCHANGE_COST)) {
rightExchangeCost = (double) anno.getValue();
}
}
// Visit the left and right branches.
left.accept(this, leftExchangeCost);
right.accept(this, rightExchangeCost);
return cardCost;
}
}