package com.tesora.dve.sql.statement.dml;
/*
* #%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.List;
import com.tesora.dve.common.MultiMap;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.sql.expression.ExpressionUtils;
import com.tesora.dve.sql.node.expression.ColumnInstance;
import com.tesora.dve.sql.node.expression.ExpressionAlias;
import com.tesora.dve.sql.node.expression.ExpressionNode;
import com.tesora.dve.sql.node.expression.NameAlias;
import com.tesora.dve.sql.node.expression.TableInstance;
import com.tesora.dve.sql.node.structural.FromTableReference;
import com.tesora.dve.sql.schema.Column;
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.transform.CopyContext;
import com.tesora.dve.sql.transform.CopyVisitor;
import com.tesora.dve.sql.transform.SchemaMapper;
import com.tesora.dve.sql.util.Functional;
import com.tesora.dve.sql.util.ListSet;
import com.tesora.dve.sql.util.UnaryFunction;
// switch all composition to use DMLStatementUtils -
// this forces us to track derived information
public class DMLStatementUtils {
public static SelectStatement composeMulti(SchemaContext sc, List<SelectStatement> inputs) throws PEException {
ListSet<PEStorageGroup> groups = new ListSet<PEStorageGroup>();
SchemaMapper fm = null;
for(SelectStatement s : inputs) {
if (!s.getOrderBys().isEmpty())
throw new PEException("Composition of order bys not supported.");
if (!s.getGroupBys().isEmpty())
throw new PEException("Composition of group bys not supported.");
if (s.getLimit() != null)
throw new PEException("Composition of limits not supported");
if (s.getMapper() == null)
throw new PEException("Composition of nontracked statements. Missing schema mapper.");
if (fm == null) fm = s.getMapper();
else if (!fm.hasSameRoot(s.getMapper()))
throw new PEException("Composition of statements with differing roots. Unable to preserve column/table mapping.");
groups.add(s.getSingleGroup(sc));
}
// validate groups - it's enough if they are all subsets of the first group
for(int i = 1; i < groups.size(); i++) {
if (!groups.get(0).isSubsetOf(sc, groups.get(i)))
throw new PEException("Composition of stmts on different groups. Found group " + groups.get(0) + " and " + groups.get(1));
}
List<DMLStatement> sources = new ArrayList<DMLStatement>();
List<SelectStatement> copies = new ArrayList<SelectStatement>();
ArrayList<ExpressionNode> projection = new ArrayList<ExpressionNode>();
ArrayList<ExpressionNode> wcs = new ArrayList<ExpressionNode>();
ArrayList<FromTableReference> tabs = new ArrayList<FromTableReference>();
AliasInformation combined = new AliasInformation();
CopyContext fcc = null;
for(SelectStatement s : inputs) {
sources.add(s);
SelectStatement c = CopyVisitor.copy(s);
if (fcc == null) fcc = c.getMapper().getCopyContext();
else fcc = CopyContext.compose(fcc, c.getMapper().getCopyContext());
copies.add(c);
projection.addAll(c.getProjection());
wcs.addAll(ExpressionUtils.decomposeAndClause(c.getWhereClause()));
tabs.addAll(c.getTables());
combined.take(c.getAliases());
}
ExpressionNode nwc = null;
if (!wcs.isEmpty()) {
if (wcs.size() == 1)
nwc = wcs.get(0);
else
nwc = ExpressionUtils.buildAnd(wcs);
}
SelectStatement out = new SelectStatement(combined)
.setTables(tabs).setProjection(projection).setWhereClause(nwc);
for(SelectStatement s : copies)
out.getDerivedInfo().take(s.getDerivedInfo());
SchemaMapper uberMapper = new SchemaMapper(sources, out, fcc);
out.setMapper(uberMapper);
return out;
}
public static SelectStatement compose(SchemaContext sc, SelectStatement left, SelectStatement right) throws PEException {
// we don't handle order bys, limits, group bys - make sure they are not present
if (!left.getOrderBys().isEmpty() || !right.getOrderBys().isEmpty())
throw new PEException("Composition of order bys not supported.");
if (!left.getGroupBys().isEmpty() || !right.getGroupBys().isEmpty())
throw new PEException("Composition of group bys not supported.");
if ((left.getLimit() != null && !left.getLimit().hasLimitOne(sc) ||
(right.getLimit() != null && !right.getLimit().hasLimitOne(sc))))
throw new PEException("Composition of limits not supported");
SchemaMapper lm = left.getMapper();
SchemaMapper rm = right.getMapper();
if (lm == null || rm == null)
throw new PEException("Composition of nontracked statements. Missing schema mapper.");
if (!lm.hasSameRoot(rm)) {
throw new PEException("Composition of statements with differing roots. Unable to preserve column/table mapping.");
}
// as a failsafe - make sure that the two stmts are on the same storage group - most of the time we have done
// something like TempTable.buildSelect on both sides.
PEStorageGroup leftGroup = left.getSingleGroup(sc);
PEStorageGroup rightGroup = right.getSingleGroup(sc);
if (!leftGroup.isSubsetOf(sc, rightGroup))
throw new PEException("Composition of stmts on different groups. Found group " + leftGroup + " and " + rightGroup);
// this really can't be a destructive thing, so we're going to make a couple of copies and fix up
// afterwards. if this was destructive, then if these select statements were used for something else there would be an error.
SelectStatement lc = CopyVisitor.copy(left);
SelectStatement rc = CopyVisitor.copy(right);
// might want to use left and right copy context here - more correct since lc and rc aren't returned
CopyContext fcc = CopyContext.compose(lc.getMapper().getCopyContext(), rc.getMapper().getCopyContext());
ArrayList<ExpressionNode> projection = new ArrayList<ExpressionNode>();
projection.addAll(lc.getProjection());
projection.addAll(rc.getProjection());
ExpressionNode nwc = null;
ArrayList<ExpressionNode> wcs = new ArrayList<ExpressionNode>();
// decompose where clauses back down to ands, then add in
ListSet<ExpressionNode> leftWC = ExpressionUtils.decomposeAndClause(lc.getWhereClause());
ListSet<ExpressionNode> rightWC = ExpressionUtils.decomposeAndClause(rc.getWhereClause());
wcs.addAll(leftWC);
wcs.addAll(rightWC);
if (!wcs.isEmpty()) {
if (wcs.size() == 1)
nwc = wcs.get(0);
else
nwc = ExpressionUtils.buildAnd(wcs);
}
// this doesn't do the right thing for the tables, but the caller is free to do whatever they want with it.
ArrayList<FromTableReference> tabs = new ArrayList<FromTableReference>();
tabs.addAll(lc.getTables());
tabs.addAll(rc.getTables());
AliasInformation combined = new AliasInformation();
combined.take(lc.getAliases());
combined.take(rc.getAliases());
SelectStatement out = new SelectStatement(combined)
.setTables(tabs).setProjection(projection).setWhereClause(nwc);
out.getDerivedInfo().take(lc.getDerivedInfo());
out.getDerivedInfo().take(rc.getDerivedInfo());
ArrayList<DMLStatement> sources = new ArrayList<DMLStatement>();
sources.add(left);
sources.add(right);
SchemaMapper uberMapper = new SchemaMapper(sources, out, fcc);
out.setMapper(uberMapper);
return out;
}
// we do this for the purposes of redistribution, so get all of the columns
public static SelectStatement convertToSelect(SchemaContext sc, UpdateStatement us) throws PEException {
UpdateStatement copy = CopyVisitor.copy(us);
ListSet<ExpressionNode> projs = new ListSet<ExpressionNode>();
TableInstance mainTable = us.getBaseTables().get(0);
for(PEColumn c : mainTable.getAbstractTable().getColumns(sc)) {
projs.add(new ColumnInstance(c,mainTable));
}
// we should generate new aliases here
SelectStatement ss = new SelectStatement(new AliasInformation())
.setTables(copy.getTables())
.setProjection(projs)
.setWhereClause(copy.getWhereClause());
ss.setOrderBy(copy.getOrderBys());
ss.setLimit(copy.getLimit());
ss.getDerivedInfo().take(copy.getDerivedInfo());
SchemaMapper mapper = new SchemaMapper(copy.getMapper().getOriginals(), ss, copy.getMapper().getCopyContext());
ss.setMapper(mapper);
return ss;
}
public static DeleteStatement convertToDelete(SchemaContext sc, UpdateStatement us) throws PEException {
if (us.getLimit() != null)
throw new PEException("Unable to convert update with limit to delete");
UpdateStatement copy = CopyVisitor.copy(us);
DeleteStatement ds = new DeleteStatement().setTruncate(false);
ds.setTables(copy.getTables())
.setWhereClause(copy.getWhereClause());
ds.getDerivedInfo().take(copy.getDerivedInfo());
SchemaMapper mapper = new SchemaMapper(copy.getMapper().getOriginals(), ds, copy.getMapper().getCopyContext());
ds.setMapper(mapper);
return ds;
}
public static UpdateStatement convertToUpdate(SelectStatement ss, UpdateStatement guide) throws PEException {
SelectStatement copy = CopyVisitor.copy(ss);
List<ExpressionNode> mappedSetExpressions = copy.getMapper().copyForward(guide.getUpdateExpressions());
UpdateStatement us = new UpdateStatement()
.setUpdateExpressions(mappedSetExpressions)
.setLimit(copy.getLimit())
.setOrderBys(copy.getOrderBys());
us.setTables(copy.getTables())
.setWhereClause(copy.getWhereClause());
SchemaMapper mapper = new SchemaMapper(copy.getMapper().getOriginals(), us, copy.getMapper().getCopyContext());
us.setMapper(mapper);
return us;
}
public static SelectStatement convertDeleteToSelect(DeleteStatement copy, MultiMap<TableInstance,Column<?>> requiredColumns) {
List<ExpressionNode> proj = new ArrayList<ExpressionNode>();
for(TableInstance ti : requiredColumns.keySet()) {
final TableInstance fti = ti;
proj.addAll(Functional.apply(requiredColumns.get(ti), new UnaryFunction<ExpressionNode, Column<?>>() {
@Override
public ExpressionNode evaluate(Column<?> object) {
return new ExpressionAlias(new ColumnInstance(object, fti), new NameAlias(object.getName().getUnqualified()), false);
}
}));
}
SelectStatement ss = new SelectStatement(new AliasInformation())
.setTables(copy.getTables()).setProjection(proj).setWhereClause(copy.getWhereClause());
ss.setOrderBy(copy.getOrderBys());
ss.setLimit(copy.getLimit());
ss.getDerivedInfo().take(copy.getDerivedInfo());
return ss;
}
}