package com.tesora.dve.sql.transform.strategy; /* * #%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.TableKey; import com.tesora.dve.sql.node.expression.ConstantExpression; import com.tesora.dve.sql.node.test.EngineConstant; import com.tesora.dve.sql.schema.DistributionKey; import com.tesora.dve.sql.schema.PEColumn; import com.tesora.dve.sql.schema.PEStorageGroup; import com.tesora.dve.sql.schema.RangeDistribution; import com.tesora.dve.sql.schema.SchemaContext; import com.tesora.dve.sql.schema.VectorRange; 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.transform.behaviors.defaults.DefaultFeatureStepBuilder; import com.tesora.dve.sql.transform.execution.DMLExplainReason; import com.tesora.dve.sql.transform.execution.DMLExplainRecord; import com.tesora.dve.sql.transform.strategy.featureplan.FeatureStep; import com.tesora.dve.sql.util.ListOfPairs; import com.tesora.dve.sql.util.ListSet; import com.tesora.dve.sql.util.Pair; /* * Applies if the query can execute on a single site via either storage group topology, * tenant distribution, or having all bcast tables */ public class SingleSiteStorageGroupTransformFactory extends TransformFactory { private static boolean disabledFlag = Boolean.getBoolean("com.tesora.dve.sql.transform.strategy.singlesite.disable"); public static void disable(boolean value) { disabledFlag = value; } public static boolean isDisabled() { return disabledFlag; } @Override public FeaturePlannerIdentifier getFeaturePlannerID() { return FeaturePlannerIdentifier.SINGLE_SITE; } public static boolean isSingleSite(SchemaContext sc, DMLStatement sp, boolean ignoremt) throws PEException { // in the mt case, we might do order by rewrites even in the single persistent site case List<PEStorageGroup> groups = sp.getStorageGroups(sc); if (groups.size() != 1) return false; // if there are order by clauses don't apply in mt mode boolean isMTMode = sc.getPolicyContext().isMTMode(); if (!ignoremt && EngineConstant.ORDERBY.has(sp) && isMTMode) return false; // check for mt where all tables are distributed on tenant column using same range Pair<TableKey,RangeDistribution> vr = isOnlyDistributedOnTenantColumn(sc,sp); if (vr != null) { // annotate the statement so we can pick it out later on sp.getBlock().store(SingleSiteStorageGroupTransformFactory.class, vr); return true; } if (isNonMTAllTableBroadcastSelectStatement(sc, sp, isMTMode)) { return true; } if (sc.getOptions() != null && sc.getOptions().isInhibitSingleSiteOptimization()) return false; return groups.get(0).isSingleSiteGroup(); } public static boolean isNonMTAllTableBroadcastSelectStatement(SchemaContext sc, DMLStatement sp, boolean isMTMode) { if (!(sp instanceof ProjectingStatement)) { return false; } if (isMTMode) { return false; } // if NOT MT and all the referenced tables are broadcast then we can // apply this transform for(TableKey tbl : EngineConstant.TABLES_INC_NESTED.getValue(sp, sc)) { if (!tbl.getAbstractTable() .getDistributionVector(sc) .isBroadcast()) { // not a broadcast table return false; } } // if we got here then the table(s) referenced are all broadcast so // let this transform handle it return true; } public static Pair<TableKey,RangeDistribution> isOnlyDistributedOnTenantColumn(SchemaContext sc, DMLStatement sp) { // check for mt where all tables are distributed on tenant column using same range if (sc.getPolicyContext().requiresMTRewrites(sp.getExecutionType())) { RangeDistribution range = null; RangeDistribution current = null; ListSet<TableKey> tables = EngineConstant.TABLES_INC_NESTED.getValue(sp,sc); boolean singleSite = true; // assume Pair<TableKey,RangeDistribution> candidate = null; for(TableKey tk : tables) { if (tk.getAbstractTable().isVirtualTable()) continue; current = tk.getAbstractTable().getDistributionVector(sc).getDistributedWhollyOnTenantColumn(sc); if (current == null) { singleSite = false; break; } else if (range == null) { range = current; candidate = new Pair<TableKey, RangeDistribution>(tk,range); } else if (!range.equals(current)) { singleSite = false; break; } } if (singleSite) return candidate; } return null; } @Override public FeatureStep plan(DMLStatement stmt, PlannerContext ipc) throws PEException { if (isDisabled()) return null; if (!isSingleSite(ipc.getContext(),stmt,false)) return null; PlannerContext context = ipc.withTransform(getFeaturePlannerID()); // special context that does not check invariants ExecutionCost cost = null; DMLExplainRecord record = null; if (context.isCosting() && stmt instanceof SelectStatement) { cost = Costing.buildBasicCost(context.getContext(), (SelectStatement)stmt, true); record = new DMLExplainRecord(DMLExplainReason.SINGLE_SITE,null,cost.getRowCount()); } else { cost = new ExecutionCost(EngineConstant.WHERECLAUSE.has(stmt),true,null,-1); record = DMLExplainReason.SINGLE_SITE.makeRecord(); } DistributionKey dk = null; FeatureStep root = null; @SuppressWarnings("unchecked") Pair<TableKey,VectorRange> any = (Pair<TableKey,VectorRange>) stmt.getBlock().getFromStorage(SingleSiteStorageGroupTransformFactory.class); if (any != null && (context.getContext().getPolicyContext().isSchemaTenant() || context.getContext().getPolicyContext().isDataTenant())) { ListOfPairs<PEColumn,ConstantExpression> values = new ListOfPairs<PEColumn,ConstantExpression>(); values.add(any.getFirst().getAbstractTable().getTenantColumn(context.getContext()), context.getContext().getPolicyContext().getTenantIDLiteral(false)); dk = any.getFirst().getAbstractTable().getDistributionVector(context.getContext()).buildDistKey(context.getContext(), any.getFirst(), values); stmt.getBlock().clearFromStorage(SingleSiteStorageGroupTransformFactory.class); } if (stmt instanceof ProjectingStatement) { root = DefaultFeatureStepBuilder.INSTANCE.buildProjectingStep(context, this, (ProjectingStatement)stmt, cost, stmt.getSingleGroup(context.getContext()), stmt.getDatabase(context.getContext()), EngineConstant.BROADEST_DISTRIBUTION_VECTOR.getValue(stmt,context.getContext()), dk, DMLExplainReason.SINGLE_SITE.makeRecord()); } else { root = DefaultFeatureStepBuilder.INSTANCE.buildStep(context, this, stmt, dk, record); } return root.withDefangInvariants(); } }