/**
* 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;
import java.util.List;
import java.util.stream.Collectors;
import org.diqube.context.AutoInstatiate;
import org.diqube.diql.request.ComparisonRequest;
import org.diqube.diql.request.ExecutionRequest;
import org.diqube.diql.request.FromRequest;
import org.diqube.diql.request.FunctionRequest;
import org.diqube.diql.request.GroupRequest;
import org.diqube.diql.request.OrderRequest;
import org.diqube.remote.cluster.thrift.RCol;
import org.diqube.remote.cluster.thrift.RColOrValue;
import org.diqube.remote.cluster.thrift.RExecutionPlan;
import org.diqube.remote.cluster.thrift.RExecutionPlanFrom;
import org.diqube.remote.cluster.thrift.RExecutionPlanFromFlattened;
import org.diqube.remote.cluster.thrift.RExecutionPlanStep;
import org.diqube.remote.cluster.thrift.RExecutionPlanStepDetailsFunction;
import org.diqube.remote.cluster.thrift.RExecutionPlanStepDetailsGroup;
import org.diqube.remote.cluster.thrift.RExecutionPlanStepDetailsOrder;
import org.diqube.remote.cluster.thrift.RExecutionPlanStepDetailsOrderCol;
import org.diqube.remote.cluster.thrift.RExecutionPlanStepDetailsOrderLimit;
import org.diqube.remote.cluster.thrift.RExecutionPlanStepDetailsResolve;
import org.diqube.remote.cluster.thrift.RExecutionPlanStepDetailsRowId;
import org.diqube.remote.cluster.thrift.RExecutionPlanStepType;
import org.diqube.thrift.base.thrift.RValue;
import org.diqube.util.ColumnOrValue;
import org.diqube.util.ColumnOrValue.Type;
import org.diqube.util.Pair;
/**
* A factory that creates remote execution plan steps from {@link ExecutionRequest} objects etc.
*
* <p>
* Remote execution plan steps are data objects managed by Thrift which can be used to remotely call cluster nodes to
* execute something.
*
* @author Bastian Gloeckle
*/
@AutoInstatiate
public class RemoteExecutionPlanFactory {
public RExecutionPlanStep createGroupIntermediaryAggregateStep(FunctionRequest fnReq, int stepId) {
return createFunctionStep(fnReq, stepId, RExecutionPlanStepType.GROUP_INTERMEDIATE_AGGREGATE);
}
public RExecutionPlanStep createColumnAggregateStep(FunctionRequest fnReq, int stepId) {
return createFunctionStep(fnReq, stepId, RExecutionPlanStepType.COLUMN_AGGREGATE);
}
public RExecutionPlanStep createProjectStep(FunctionRequest fnReq, int stepId) {
return createFunctionStep(fnReq, stepId, RExecutionPlanStepType.PROJECT);
}
public RExecutionPlanStep createRepeatedProjectStep(FunctionRequest fnReq, int stepId) {
return createFunctionStep(fnReq, stepId, RExecutionPlanStepType.REPEATED_PROJECT);
}
private RExecutionPlanStep createFunctionStep(FunctionRequest fnReq, int stepId, RExecutionPlanStepType type) {
RExecutionPlanStep functionStep = new RExecutionPlanStep();
functionStep.setStepId(stepId);
functionStep.setType(type);
functionStep.setDetailsFunction(new RExecutionPlanStepDetailsFunction());
functionStep.getDetailsFunction().setFunctionNameLowerCase(fnReq.getFunctionName());
functionStep.getDetailsFunction().setResultColumn(new RCol(RCol._Fields.COL_NAME, fnReq.getOutputColumn()));
for (ColumnOrValue param : fnReq.getInputParameters()) {
if (param.getType().equals(ColumnOrValue.Type.COLUMN)) {
functionStep.getDetailsFunction().addToFunctionArguments(
new RColOrValue(RColOrValue._Fields.COLUMN, new RCol(RCol._Fields.COL_NAME, param.getColumnName())));
} else {
functionStep.getDetailsFunction()
.addToFunctionArguments(new RColOrValue(RColOrValue._Fields.VALUE, parseRValue(param.getValue())));
}
}
return functionStep;
}
private RValue parseRValue(Object value) {
RValue res = new RValue();
if (value instanceof String)
res.setStrValue((String) value);
else if (value instanceof Long)
res.setLongValue((Long) value);
else if (value instanceof Double)
res.setDoubleValue((Double) value);
return res;
}
public RExecutionPlanStep createRowIdComparison(ComparisonRequest.Leaf comparisonLeaf, int stepId,
RExecutionPlanStepType type) {
RExecutionPlanStep step = new RExecutionPlanStep();
step.setStepId(stepId);
step.setType(type);
step.setDetailsRowId(new RExecutionPlanStepDetailsRowId());
String leftColName = comparisonLeaf.getLeftColumnName();
ColumnOrValue right = comparisonLeaf.getRight();
step.getDetailsRowId().setColumn(new RCol(RCol._Fields.COL_NAME, leftColName));
if (right.getType().equals(Type.COLUMN))
step.getDetailsRowId().setOtherColumn(new RCol(RCol._Fields.COL_NAME, right.getColumnName()));
else
// TODO #22 support multiple equal values
step.getDetailsRowId().addToSortedValues(parseRValue(right.getValue()));
return step;
}
public RExecutionPlanStep createRowIdAnd(int stepId) {
RExecutionPlanStep step = new RExecutionPlanStep();
step.setStepId(stepId);
step.setType(RExecutionPlanStepType.ROW_ID_AND);
return step;
}
public RExecutionPlanStep createRowIdOr(int stepId) {
RExecutionPlanStep step = new RExecutionPlanStep();
step.setStepId(stepId);
step.setType(RExecutionPlanStepType.ROW_ID_OR);
return step;
}
public RExecutionPlanStep createRowIdNot(int stepId) {
RExecutionPlanStep step = new RExecutionPlanStep();
step.setStepId(stepId);
step.setType(RExecutionPlanStepType.ROW_ID_NOT);
return step;
}
public RExecutionPlanStep createRowIdSink(int stepId) {
RExecutionPlanStep step = new RExecutionPlanStep();
step.setStepId(stepId);
step.setType(RExecutionPlanStepType.ROW_ID_SINK);
return step;
}
public RExecutionPlanStep createGroup(GroupRequest groupRequest, int stepId) {
RExecutionPlanStep step = new RExecutionPlanStep();
step.setStepId(stepId);
step.setType(RExecutionPlanStepType.GROUP);
step.setDetailsGroup(new RExecutionPlanStepDetailsGroup());
step.getDetailsGroup().setGroupByColumns(groupRequest.getGroupColumns().stream()
.map(colName -> new RCol(RCol._Fields.COL_NAME, colName)).collect(Collectors.toList()));
return step;
}
public RExecutionPlanStep createOrder(OrderRequest orderRequest, int stepId) {
RExecutionPlanStep step = new RExecutionPlanStep();
step.setStepId(stepId);
step.setType(RExecutionPlanStepType.ORDER);
step.setDetailsOrder(new RExecutionPlanStepDetailsOrder());
for (Pair<String, Boolean> orderCol : orderRequest.getColumns()) {
RExecutionPlanStepDetailsOrderCol remoteOrderCol = new RExecutionPlanStepDetailsOrderCol();
remoteOrderCol.setColumn(new RCol(RCol._Fields.COL_NAME, orderCol.getLeft()));
remoteOrderCol.setSortAscending(orderCol.getRight());
step.getDetailsOrder().addToOrderColumns(remoteOrderCol);
}
if (orderRequest.getLimit() != null) {
step.getDetailsOrder().setLimit(new RExecutionPlanStepDetailsOrderLimit());
step.getDetailsOrder().getLimit().setLimit(orderRequest.getLimit());
if (orderRequest.getLimitStart() != null) {
step.getDetailsOrder().getLimit().setLimitStart(orderRequest.getLimitStart());
}
} else if (orderRequest.getSoftLimit() != null) {
step.getDetailsOrder().setSoftLimit(orderRequest.getSoftLimit());
}
return step;
}
public RExecutionPlanStep createColumnDictId(String colName, int stepId) {
RExecutionPlanStep step = new RExecutionPlanStep();
step.setStepId(stepId);
step.setType(RExecutionPlanStepType.RESOLVE_COLUMN_DICT_IDS);
step.setDetailsResolve(new RExecutionPlanStepDetailsResolve());
step.getDetailsResolve().setColumn(new RCol(RCol._Fields.COL_NAME, colName));
return step;
}
public RExecutionPlanStep createResolveColumnDictId(String colName, int stepId) {
// TODO #19 support resolving constants
return createColumnDictId(colName, stepId);
}
public RExecutionPlanStep createResolveValues(int stepId) {
RExecutionPlanStep step = new RExecutionPlanStep();
step.setStepId(stepId);
step.setType(RExecutionPlanStepType.RESOLVE_VALUES);
return step;
}
public RExecutionPlan createExecutionPlan(List<RExecutionPlanStep> steps, FromRequest fromRequest) {
RExecutionPlan res = new RExecutionPlan();
res.setSteps(steps);
RExecutionPlanFrom from = new RExecutionPlanFrom();
res.setFromSpec(from);
if (fromRequest.isFlattened()) {
RExecutionPlanFromFlattened flattened = new RExecutionPlanFromFlattened();
from.setFlattened(flattened);
flattened.setTableName(fromRequest.getTable());
flattened.setFlattenBy(fromRequest.getFlattenByField());
// Cannot be called yet: flattened.setFlattenId(). This will be filled in automatically by the
// ExecuteRemotePlanOnShardsStep.
} else {
from.setPlainTableName(fromRequest.getTable());
}
return res;
}
}