package com.tesora.dve.sql.raw; /* * #%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.LinkedHashMap; import java.util.List; import java.util.Map; import com.tesora.dve.db.GenericSQLCommand; import com.tesora.dve.exceptions.PEException; import com.tesora.dve.resultset.IntermediateResultSet; import com.tesora.dve.sql.ParserException.Pass; import com.tesora.dve.sql.SchemaException; import com.tesora.dve.sql.parser.CandidateParser; import com.tesora.dve.sql.parser.ExtractedLiteral; import com.tesora.dve.sql.parser.SourceLocation; import com.tesora.dve.sql.raw.jaxb.DMLStepType; import com.tesora.dve.sql.raw.jaxb.DMLType; import com.tesora.dve.sql.raw.jaxb.DistVectColumn; import com.tesora.dve.sql.raw.jaxb.DistributionType; import com.tesora.dve.sql.raw.jaxb.DynamicGroupType; import com.tesora.dve.sql.raw.jaxb.KeyType; import com.tesora.dve.sql.raw.jaxb.ParameterType; import com.tesora.dve.sql.raw.jaxb.ProjectingStepType; import com.tesora.dve.sql.raw.jaxb.Rawplan; import com.tesora.dve.sql.raw.jaxb.StepType; import com.tesora.dve.sql.raw.jaxb.TargetTableType; import com.tesora.dve.sql.schema.DistributionVector; import com.tesora.dve.sql.schema.PEColumn; import com.tesora.dve.sql.schema.PEDynamicGroup; import com.tesora.dve.sql.schema.PEKey; import com.tesora.dve.sql.schema.PEKeyColumnBase; import com.tesora.dve.sql.schema.PEStorageGroup; import com.tesora.dve.sql.schema.PETable; import com.tesora.dve.sql.schema.SchemaContext; import com.tesora.dve.sql.schema.cache.IDelegatingLiteralExpression; import com.tesora.dve.sql.statement.Statement; import com.tesora.dve.sql.transform.execution.AbstractProjectingExecutionStep; import com.tesora.dve.sql.transform.execution.DeleteExecutionStep; import com.tesora.dve.sql.transform.execution.DirectExecutionStep; import com.tesora.dve.sql.transform.execution.ExecutionPlan; import com.tesora.dve.sql.transform.execution.ExecutionStep; import com.tesora.dve.sql.transform.execution.HasPlanning; import com.tesora.dve.sql.transform.execution.RedistributionExecutionStep; import com.tesora.dve.sql.transform.execution.TransactionExecutionStep; import com.tesora.dve.sql.transform.execution.UpdateExecutionStep; import com.tesora.dve.sql.util.UnaryProcedure; public final class ExecToRawConverter { public static IntermediateResultSet convertForRawExplain(SchemaContext sc, ExecutionPlan ep, Statement orig, String origSQL) { return RawUtils.buildRawExplainResults(convert(sc, ep,orig,origSQL)); } public static Rawplan convert(SchemaContext sc, ExecutionPlan ep, Statement orig, String origSQL) { if (origSQL == null) throw new SchemaException(Pass.PLANNER,"Raw plan conversion requires original sql"); ExecToRawConverter c = new ExecToRawConverter(sc, ep,orig,origSQL); return c.getRaw(); } private final ExecutionPlan thePlan; private final String originalSQL; private final SchemaContext sc; private final Rawplan raw; private HashMap<IDelegatingLiteralExpression, ParameterType> parameters; private LinkedHashMap<PEStorageGroup,DynamicGroupType> dynGroups; public ExecToRawConverter(SchemaContext pc, ExecutionPlan ep, Statement orig, String origSQL) { raw = new Rawplan(); thePlan = ep; sc = pc; String sql = origSQL; SourceLocation sloc = orig.getSourceLocation(); int offset = sloc.getPositionInLine(); if (offset > -1) sql = origSQL.substring(offset); originalSQL = sql; parameters = new HashMap<IDelegatingLiteralExpression, ParameterType>(); dynGroups = new LinkedHashMap<PEStorageGroup,DynamicGroupType>(); convert(); } public Rawplan getRaw() { return raw; } private void convert() { defineMatchAndVars(); defineGroupsAndSteps(); } private void defineMatchAndVars() { // take a stmt of the form select a.id from A a where a.pid = 15 limit 2 // into select a.id from A a where a.pid = @p1 limit @p2 // and record types for variables. CandidateParser cp = new CandidateParser(originalSQL); if (!cp.shrink()) throw new SchemaException(Pass.PLANNER, "Unable to shrink supposed cacheable input for raw explain"); List<ExtractedLiteral> shrunkLiterals = cp.getLiterals(); HashMap<ExtractedLiteral,ParameterType> paramForLit = new HashMap<ExtractedLiteral,ParameterType>(); List<IDelegatingLiteralExpression> literals = thePlan.getValueManager().getRawLiterals(); for(IDelegatingLiteralExpression idle : literals) { if (idle.getPosition() == 0) continue; int pos = idle.getPosition() - 1; ExtractedLiteral ex = shrunkLiterals.get(pos); ParameterType pt = new ParameterType(); pt.setName("p" + pos); pt.setType(EnumConverter.convert(ex.getType())); parameters.put(idle,pt); paramForLit.put(ex, pt); } StringBuilder buf = new StringBuilder(); String sbuf = cp.getShrunk(); ExtractedLiteral prev = null; for(ExtractedLiteral el : shrunkLiterals) { int prevIndex = (prev == null ? 0 : prev.getFinalOffset() + 1); int curIndex = el.getFinalOffset(); buf.append(sbuf.substring(prevIndex, curIndex)); buf.append("@").append(paramForLit.get(el).getName()); prev = el; } if (prev != null) buf.append(sbuf.substring(prev.getFinalOffset()+1)); else buf.append(originalSQL); String match = buf.toString(); raw.setInsql(match); for(ExtractedLiteral el : shrunkLiterals) { ParameterType pt = paramForLit.get(el); raw.getParameter().add(pt); } } private void defineGroupsAndSteps() { final List<ExecutionStep> stepsInOrder = new ArrayList<ExecutionStep>(); thePlan.visitInExecutionOrder(new UnaryProcedure<HasPlanning>() { @Override public void execute(HasPlanning object) { stepsInOrder.add((ExecutionStep) object); } }); for(ExecutionStep es : stepsInOrder) { PEStorageGroup src = es.getPEStorageGroup(); maybeAccDynGroup(src); if (es instanceof RedistributionExecutionStep) { RedistributionExecutionStep pes = (RedistributionExecutionStep) es; maybeAccDynGroup(pes.getTargetGroup(sc,sc.getValues())); } } for(DynamicGroupType dgt : dynGroups.values()) { raw.getDyngroup().add(dgt); } for(ExecutionStep es : stepsInOrder) { StepType out = null; if (es instanceof AbstractProjectingExecutionStep) { try { out = buildProjectingStep((AbstractProjectingExecutionStep) es); } catch (final PEException e) { throw new SchemaException(Pass.PLANNER, e); } } else if (es instanceof UpdateExecutionStep || es instanceof DeleteExecutionStep) { out = buildUpdateStep((DirectExecutionStep)es); } else if (es instanceof TransactionExecutionStep) { out = buildTransactionStep((TransactionExecutionStep)es); } else { throw new SchemaException(Pass.PLANNER, "Unable to convert to raw plan step for " + es.getClass().getSimpleName()); } if (out == null) throw new SchemaException(Pass.PLANNER, "Unable to build raw plan step for " + es.getClass().getSimpleName()); raw.getStep().add(out); } } private void maybeAccDynGroup(PEStorageGroup g) { if (g == null) return; if (!g.isTempGroup()) return; PEDynamicGroup dynGroup = (PEDynamicGroup) g.getPEStorageGroup(sc,sc.getValues()); DynamicGroupType e = dynGroups.get(dynGroup); if (e == null) { e = new DynamicGroupType(); e.setName("dg" + dynGroups.size()); e.setSize(EnumConverter.convert(dynGroup.getScale())); dynGroups.put(dynGroup, e); } } private StepType buildProjectingStep(AbstractProjectingExecutionStep pes) throws PEException { ProjectingStepType out = new ProjectingStepType(); if (pes instanceof RedistributionExecutionStep) { RedistributionExecutionStep redist = (RedistributionExecutionStep) pes; out.setTarget(buildTargetTable(redist.getTargetTable())); } fillDML(out, pes, DMLType.PROJECTING); return out; } private StepType buildUpdateStep(DirectExecutionStep des) { return null; } private StepType buildTransactionStep(TransactionExecutionStep tes) { return null; } private void fillDML(DMLStepType dmlt, DirectExecutionStep des, DMLType action) throws PEException { dmlt.setAction(action); PEStorageGroup src = des.getPEStorageGroup(); PEStorageGroup actual = src.getPEStorageGroup(sc,sc.getValues()); dmlt.setSrcgrp(getGroupName(actual)); DistributionVector dv = des.getDistributionVector(); if (dv != null) dmlt.setSrcmod(EnumConverter.convert(dv.getModel())); GenericSQLCommand gsql = des.getRawSQL(); dmlt.setSrcsql(parameterize(gsql)); } private TargetTableType buildTargetTable(PETable tab) { TargetTableType out = new TargetTableType(); out.setName(tab.getName(sc,sc.getValues()).getUnquotedName().get()); out.setTemp(tab.isTempTable()); PEStorageGroup ofGroup = tab.getStorageGroup(sc); PEStorageGroup actual = ofGroup.getPEStorageGroup(sc,sc.getValues()); out.setGroup(getGroupName(actual)); if (tab.isTempTable()) { out.setDistvect(buildDistributionType(tab.getDistributionVector(sc))); // also, declare keys for(PEKey pek : tab.getKeys(sc)) out.getKey().add(buildKey(pek)); } return out; } private String getGroupName(PEStorageGroup actual) { if (actual.isTempGroup()) { DynamicGroupType dgt = dynGroups.get(actual); if (dgt == null) throw new SchemaException(Pass.PLANNER, "Internal error: unknown dynamic group"); return dgt.getName(); } else { return actual.getPersistent(sc,sc.getValues()).getName(); } } DistributionType buildDistributionType(DistributionVector dvect) { DistributionType out = new DistributionType(); out.setModel(EnumConverter.convert(dvect.getModel())); if (dvect.isRange()) out.setRange(dvect.getRangeDistribution().getName().getUnquotedName().get()); if (dvect.getModel().getUsesColumns()) { List<PEColumn> cols = dvect.getColumns(sc); for(int i = 0; i < cols.size(); i++) { DistVectColumn dvc = new DistVectColumn(); dvc.setName(cols.get(i).getName().getUnquotedName().get()); dvc.setPosition(i); out.getColumn().add(dvc); } } return out; } private KeyType buildKey(PEKey pek) { KeyType kt = new KeyType(); kt.setName(pek.getName().getUnqualified().getUnquotedName().get()); if (pek.getConstraint() != null) kt.setConstraint(pek.getConstraint().getSQL()); if (pek.getType() != null) kt.setType(pek.getType().getSQL()); for(PEKeyColumnBase pekc : pek.getKeyColumns()) { kt.getColumn().add(pekc.getName().getUnqualified().getUnquotedName().get()); } return kt; } private String parameterize(GenericSQLCommand in) throws PEException { Map<Integer, String> mapping = new HashMap<Integer, String>(); for (Map.Entry<IDelegatingLiteralExpression, ParameterType> me : parameters.entrySet()) { mapping.put(me.getKey().getPosition(), "@" + me.getValue().getName()); } GenericSQLCommand p = in.resolveRawEntries(mapping, sc.getValues()); return p.getDecoded(); } }