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.List; 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.expression.ColumnInstance; import com.tesora.dve.sql.node.expression.ExpressionNode; import com.tesora.dve.sql.node.expression.FunctionCall; import com.tesora.dve.sql.node.expression.Subquery; import com.tesora.dve.sql.node.structural.LimitSpecification; import com.tesora.dve.sql.node.test.EngineConstant; import com.tesora.dve.sql.schema.DistributionVector.Model; import com.tesora.dve.sql.schema.PEStorageGroup; import com.tesora.dve.sql.schema.SchemaContext; 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.statement.dml.SelectStatement; import com.tesora.dve.sql.statement.dml.UnionStatement; import com.tesora.dve.sql.transform.AggFunCollector; import com.tesora.dve.sql.transform.ColumnInstanceCollector; import com.tesora.dve.sql.transform.strategy.PlannerContext; import com.tesora.dve.sql.util.ListSet; 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.transform.strategy.featureplan.RedistributionFlags; public abstract class NestingStrategy { public static enum ScalarCheckResult { IN_EXISTS_FCN(true, "The query is called within an exists function which returns a scalar."), IS_UNION_STATEMENT(false, "The statement is a union."), HAS_TOO_MANY_COLUMNS(false, "The query returns more than one column."), IN_AGGREGATION_FCN(true, "The query returns a single column in an aggregation function, without a group by clause."), IS_LITERAL(true, "The query returns a single literal expression."), HAS_LIMIT_ONE(true, "The query returns a single column and has a limit of one."), UNKNOWN(false, "We cannot prove this query has a scalar result."); private final boolean valid; private final String describtion; private ScalarCheckResult(final boolean valid, final String describtion) { this.valid = valid; this.describtion = describtion; } public boolean isValid() { return this.valid; } public String getDescribtion() { return this.describtion; } } protected ExpressionPath pathWithinEnclosing; protected Subquery sq; protected ProjectingStatement nested; protected FeatureStep planned; public NestingStrategy(Subquery nested, ExpressionPath pathTo) { this.sq = nested; this.pathWithinEnclosing = pathTo; this.nested = sq.getStatement(); } public Subquery getSubquery() { return sq; } public ExpressionPath getPathWithinEnclosing() { return pathWithinEnclosing; } public void setStep(FeatureStep planned) { this.planned = planned; } public FeatureStep getStep() { return this.planned; } public DMLStatement beforeChildPlanning(SchemaContext sc, DMLStatement parent) throws PEException { return null; } public DMLStatement afterChildPlanning(PlannerContext pc, DMLStatement parent, DMLStatement preREwrites, FeaturePlanner planner, List<FeatureStep> childSteps) throws PEException { return null; } public FeatureStep afterParentPlanning(PlannerContext pc, FeatureStep parentStep, FeaturePlanner planner, List<FeatureStep> childSteps) throws PEException { return null; } public static ScalarCheckResult hasScalarResult(SchemaContext sc, ProjectingStatement ps) { // if the statement is called within an exists function - we don't actually require a scalar result. if (EngineConstant.FUNCTION.has(ps.getParent().getParent(), EngineConstant.EXISTS)) { return ScalarCheckResult.IN_EXISTS_FCN; } if (ps instanceof UnionStatement) { return ScalarCheckResult.IS_UNION_STATEMENT; } final SelectStatement ss = (SelectStatement) ps; // a scalar query is one which returns a single value - so it has one column, and that column will have one value if (ss.getProjectionEdge().size() > 1) { return ScalarCheckResult.HAS_TOO_MANY_COLUMNS; } final ExpressionNode singleColumn = ExpressionUtils.getTarget(ss.getProjectionEdge().get(0)); final ListSet<FunctionCall> anyAggs = AggFunCollector.collectAggFuns(singleColumn); if (!anyAggs.isEmpty() && !EngineConstant.GROUPBY.has(ss)) { // single column aggr, without a group by - must be a grand agg // (i.e. max(id)) return ScalarCheckResult.IN_AGGREGATION_FCN; } // could be a literal, check for that too ListSet<ColumnInstance> anyColumns = ColumnInstanceCollector.getColumnInstances(singleColumn); if (anyColumns.isEmpty()) { return ScalarCheckResult.IS_LITERAL; } if (hasLimitOne(sc, ss)) { return ScalarCheckResult.HAS_LIMIT_ONE; } return ScalarCheckResult.UNKNOWN; } public static boolean hasLimitOne(final SchemaContext sc, final SelectStatement select) { final LimitSpecification limitSpec = select.getLimit(); return ((limitSpec != null) && limitSpec.hasLimitOne(sc)); } protected RedistFeatureStep buildChildBCastTempTableStep(PlannerContext pc, ProjectingFeatureStep pfs, List<PEStorageGroup> enclosingGroups, FeaturePlanner planner, RedistributionFlags flags) throws PEException { PEStorageGroup targetGroup = null; if (enclosingGroups.size() == 1) targetGroup = enclosingGroups.get(0); else targetGroup = pc.getTempGroupManager().getGroup(pfs.getCost().getGroupScore()); return pfs.redist(pc, planner, new TempTableCreateOptions(Model.BROADCAST,targetGroup) .withRowCount(pfs.getCost().getRowCount()), flags, null); } }