/**
* diqube: Distributed Query Base.
*
* Copyright (C) 2015 Bastian Gloeckle
*
* This file is part of diqube.
*
* diqube 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 org.diqube.plan.planner;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import org.diqube.diql.request.ComparisonRequest;
import org.diqube.diql.request.ComparisonRequest.And;
import org.diqube.diql.request.ComparisonRequest.Leaf;
import org.diqube.diql.request.ComparisonRequest.Not;
import org.diqube.diql.request.ComparisonRequest.Operator;
import org.diqube.diql.request.ComparisonRequest.Or;
import org.diqube.execution.ExecutablePlanFactory;
import org.diqube.execution.ExecutablePlanStep;
import org.diqube.execution.consumers.OverwritingRowIdConsumer;
import org.diqube.execution.steps.RowIdInequalStep;
import org.diqube.execution.steps.RowIdInequalStep.RowIdComparator;
import org.diqube.executionenv.ExecutionEnvironment;
import org.diqube.util.ColumnOrValue;
import org.diqube.util.Pair;
import org.diqube.util.Triple;
/**
* Builder that handles {@link ComparisonRequest}s in a HAVING clause - these are fully handled by the query master.
*
* @author Bastian Gloeckle
*/
public class HavingBuilder implements ComparisonRequestBuilder<ExecutablePlanStep> {
private ExecutablePlanFactory executablePlanFactory;
private Supplier<Integer> nextMasterStepIdSupplier;
private ColumnManager<ExecutablePlanStep> columnManager;
private ExecutionEnvironment env;
private MasterWireManager masterWireManager;
public HavingBuilder(ExecutablePlanFactory executablePlanFactory, Supplier<Integer> nextMasterStepIdSupplier,
ColumnManager<ExecutablePlanStep> columnManager, ExecutionEnvironment env, MasterWireManager masterWireManager) {
this.executablePlanFactory = executablePlanFactory;
this.nextMasterStepIdSupplier = nextMasterStepIdSupplier;
this.columnManager = columnManager;
this.env = env;
this.masterWireManager = masterWireManager;
}
@Override
public Pair<ExecutablePlanStep, List<ExecutablePlanStep>> build(ComparisonRequest comparisonRoot) {
List<ExecutablePlanStep> allHavingSteps = new ArrayList<>();
ExecutablePlanStep havingRootStep = ComparisonRequestUtil.walkComparisonTreeAndCreate(comparisonRoot,
new Function<Triple<ComparisonRequest, ExecutablePlanStep, ExecutablePlanStep>, ExecutablePlanStep>() {
@Override
public ExecutablePlanStep apply(Triple<ComparisonRequest, ExecutablePlanStep, ExecutablePlanStep> t) {
ExecutablePlanStep res = null;
if (t.getLeft() instanceof Leaf) {
Leaf leaf = (Leaf) t.getLeft();
if (leaf.getOp().equals(Operator.EQ)) {
String colName = leaf.getLeftColumnName();
if (leaf.getRight().getType().equals(ColumnOrValue.Type.COLUMN))
res = executablePlanFactory.createRowIdEqualsStep(nextMasterStepIdSupplier.get(), env, colName,
leaf.getRight().getColumnName());
else {
Object[] values = (Object[]) Array.newInstance(leaf.getRight().getValue().getClass(), 1);
values[0] = leaf.getRight().getValue();
res =
executablePlanFactory.createRowIdEqualsStep(nextMasterStepIdSupplier.get(), env, colName, values);
}
} else {
RowIdComparator comparator = null;
switch (leaf.getOp()) {
case GT_EQ:
comparator = new RowIdInequalStep.GtEqRowIdComparator();
break;
case GT:
comparator = new RowIdInequalStep.GtRowIdComparator();
break;
case LT_EQ:
comparator = new RowIdInequalStep.LtEqRowIdComparator();
break;
case LT:
comparator = new RowIdInequalStep.LtRowIdComparator();
break;
default:
}
String colName = leaf.getLeftColumnName();
if (leaf.getRight().getType().equals(ColumnOrValue.Type.COLUMN))
res = executablePlanFactory.createRowIdInequalStep2Cols(nextMasterStepIdSupplier.get(), env, colName,
leaf.getRight().getColumnName(), comparator);
else
res = executablePlanFactory.createRowIdInequalStep(nextMasterStepIdSupplier.get(), env, colName,
leaf.getRight().getValue(), comparator);
}
// we're on the query master when executing this and the having clause is definitely based on aggregated
// columns (this is validated that way!). This means that the aggregation columns and the projected
// columns based on this should already be available on the query master. So we only need to wire
// correctly, we do not need to "ensure columns are available".
columnManager.wireOutputOfColumnIfAvailable(leaf.getLeftColumnName(), res);
if (leaf.getRight().getType().equals(ColumnOrValue.Type.COLUMN))
columnManager.wireOutputOfColumnIfAvailable(leaf.getRight().getColumnName(), res);
allHavingSteps.add(res);
return res;
} else if (t.getLeft() instanceof Not) {
ExecutablePlanStep childStep = t.getMiddle();
res = executablePlanFactory.createOverwritingRowIdNotStep(nextMasterStepIdSupplier.get());
masterWireManager.wire(OverwritingRowIdConsumer.class, childStep, res);
allHavingSteps.add(res);
return res;
} else if (t.getLeft() instanceof And) {
res = executablePlanFactory.createOverwritingRowIdAndStep(nextMasterStepIdSupplier.get());
} else if (t.getLeft() instanceof Or) {
res = executablePlanFactory.createOverwritingRowIdOrStep(nextMasterStepIdSupplier.get());
}
// wire row ID flow.
masterWireManager.wire(OverwritingRowIdConsumer.class, t.getMiddle(), res);
masterWireManager.wire(OverwritingRowIdConsumer.class, t.getRight(), res);
allHavingSteps.add(res);
return res;
}
});
return new Pair<>(havingRootStep, allHavingSteps);
}
}