package com.tesora.dve.sql.transform.strategy.nested;
/*
* #%L
* Tesora Inc.
* Database Virtualization Engine
* %%
* Copyright (C) 2011 - 2014 Tesora Inc.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
* #L%
*/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.sql.expression.ExpressionPath;
import com.tesora.dve.sql.expression.ExpressionUtils;
import com.tesora.dve.sql.node.LanguageNode;
import com.tesora.dve.sql.node.Traversal;
import com.tesora.dve.sql.node.expression.ColumnInstance;
import com.tesora.dve.sql.node.expression.ExpressionNode;
import com.tesora.dve.sql.node.expression.Subquery;
import com.tesora.dve.sql.node.expression.TableInstance;
import com.tesora.dve.sql.node.expression.TempTableInstance;
import com.tesora.dve.sql.node.test.EdgeTest;
import com.tesora.dve.sql.node.test.EngineConstant;
import com.tesora.dve.sql.schema.DistributionVector;
import com.tesora.dve.sql.schema.DistributionVector.Model;
import com.tesora.dve.sql.schema.PEAbstractTable;
import com.tesora.dve.sql.schema.PEColumn;
import com.tesora.dve.sql.schema.PEStorageGroup;
import com.tesora.dve.sql.schema.SchemaContext;
import com.tesora.dve.sql.schema.TempTable;
import com.tesora.dve.sql.schema.TempTableCreateOptions;
import com.tesora.dve.sql.statement.dml.DMLStatement;
import com.tesora.dve.sql.statement.dml.ProjectingStatement;
import com.tesora.dve.sql.transform.strategy.PlannerContext;
import com.tesora.dve.sql.transform.strategy.featureplan.FeaturePlanner;
import com.tesora.dve.sql.transform.strategy.featureplan.FeatureStep;
import com.tesora.dve.sql.transform.strategy.featureplan.ProjectingFeatureStep;
import com.tesora.dve.sql.transform.strategy.featureplan.RedistFeatureStep;
import com.tesora.dve.sql.util.ListSet;
/*
* A from clause subquery is converted into a temp table, which is then pasted back into the parent query.
*/
public class HandleFromClauseSubquery extends StrategyFactory {
@Override
public NestingStrategy adapt(SchemaContext sc, EdgeTest location,
DMLStatement enclosing, Subquery sq, ExpressionPath path)
throws PEException {
if (EngineConstant.FROMCLAUSE == location)
return new FromClauseHandler(sq, path);
return null;
}
protected static class FromClauseHandler extends NestingStrategy {
public FromClauseHandler(Subquery nested, ExpressionPath pathTo) {
super(nested, pathTo);
}
@Override
public DMLStatement afterChildPlanning(PlannerContext pc, DMLStatement parent, DMLStatement preRewrites,
FeaturePlanner planner, List<FeatureStep> collector) throws PEException {
final SchemaContext sc = pc.getContext();
ExpressionPath ep = getPathWithinEnclosing();
ProjectingStatement origChild = (ProjectingStatement) ep.apply(parent);
// the parent is a subquery node, so we want the parent of the parent
Subquery sq = (Subquery) origChild.getParent();
RedistFeatureStep rfs = buildChildTempTable(pc, planner);
setStep(rfs);
collector.add(rfs);
TempTable ct = rfs.getTargetTempTable();
// now - we originally had a subquery; now we're going to build a new table instance from
// the temp table and replace the subquery with the table instance
TableInstance ti = new TempTableInstance(sc,ct,sq.getAlias().getUnqualified());
sq.getParentEdge().set(ti);
parent.getDerivedInfo().getLocalNestedQueries().remove(origChild);
parent.getDerivedInfo().addLocalTable(ti.getTableKey());
parent.getDerivedInfo().removeLocalTable(sq.getTable());
// build a forwarding table for columns
HashMap<PEColumn,PEColumn> columnForwarding = new HashMap<PEColumn,PEColumn>();
List<PEColumn> origColumns = sq.getTable().getColumns(sc);
List<PEColumn> newColumns = ct.getColumns(sc);
if (origColumns.size() != newColumns.size())
throw new PEException("Mismatched columns after subquery planning, had " + origColumns.size() + ", but now have " + newColumns.size());
for(int i = 0; i < origColumns.size(); i++) {
columnForwarding.put(origColumns.get(i), newColumns.get(i));
}
parent = (DMLStatement) new TableForwarder(columnForwarding,sq.getTable(),ti).traverse(parent);
return parent;
}
protected RedistFeatureStep buildChildTempTable(PlannerContext pc, FeaturePlanner planner) throws PEException {
ProjectingFeatureStep pfs = (ProjectingFeatureStep) planned;
ProjectingStatement nss = (ProjectingStatement) pfs.getPlannedStatement();
PEStorageGroup tg = pc.getTempGroupManager().getGroup(pfs.getCost().getGroupScore());
List<ExpressionNode> proj = nss.getProjections().get(0);
ListSet<PEColumn> projColumns = new ListSet<PEColumn>();
for(ExpressionNode en : proj) {
ExpressionNode targ = ExpressionUtils.getTarget(en);
if (targ instanceof ColumnInstance) {
ColumnInstance ci = (ColumnInstance) targ;
projColumns.add(ci.getPEColumn());
}
}
List<Integer> distKey = new ArrayList<Integer>();
DistributionVector dv = EngineConstant.BROADEST_DISTRIBUTION_VECTOR.getValue(nss, pc.getContext());
if (dv != null && dv.getModel() == Model.STATIC) {
for(PEColumn col : dv.getColumns(pc.getContext())) {
if (projColumns.contains(col))
distKey.add(col.getPosition());
}
}
if (distKey.isEmpty()) {
for(int i = 0; i < proj.size(); i++)
distKey.add(i);
}
return pfs.redist(pc,
planner,
new TempTableCreateOptions(Model.STATIC,tg)
.withRowCount(pfs.getCost().getRowCount())
.distributeOn(distKey),
null,
null);
}
}
private static class TableForwarder extends Traversal {
private Map<PEColumn, PEColumn> columnForwarding;
private PEAbstractTable<?> sqt;
private TableInstance nti;
public TableForwarder(Map<PEColumn,PEColumn> forwarding, PEAbstractTable<?> origTable, TableInstance nti) {
super(Order.POSTORDER, ExecStyle.ONCE);
columnForwarding = forwarding;
sqt = origTable;
this.nti = nti;
}
@Override
public LanguageNode action(LanguageNode in) {
if (in instanceof ColumnInstance) {
ColumnInstance ci = (ColumnInstance) in;
PEColumn nc = columnForwarding.get(ci.getColumn());
if (nc != null) {
if (ci.getTableInstance().getTable() == sqt) {
return new ColumnInstance(nc,nti);
}
}
}
return in;
}
}
}