package com.tesora.dve.sql.transform.execution;
/*
* #%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.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import com.tesora.dve.db.Emitter.EmitOptions;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.queryplan.QueryStepOperation;
import com.tesora.dve.resultset.ColumnSet;
import com.tesora.dve.resultset.IntermediateResultSet;
import com.tesora.dve.resultset.ProjectionInfo;
import com.tesora.dve.resultset.ResultRow;
import com.tesora.dve.server.connectionmanager.SSConnection;
import com.tesora.dve.sql.ParserException.Pass;
import com.tesora.dve.sql.SchemaException;
import com.tesora.dve.sql.raw.ExecToRawConverter;
import com.tesora.dve.sql.schema.ConnectionValues;
import com.tesora.dve.sql.schema.ExplainOptions;
import com.tesora.dve.sql.schema.SchemaContext;
import com.tesora.dve.sql.schema.ValueManager;
import com.tesora.dve.sql.schema.cache.CacheInvalidationRecord;
import com.tesora.dve.sql.statement.Statement;
import com.tesora.dve.sql.util.ListSet;
import com.tesora.dve.sql.util.UnaryProcedure;
public abstract class ExecutionPlan implements HasPlanning {
private static final Logger logger = Logger.getLogger( RootExecutionPlan.class );
protected final ExecutionSequence steps;
protected final ValueManager values;
protected final ListSet<ExecutionPlan> nested;
public ExecutionPlan(ValueManager valueManager) {
this.values = valueManager;
this.nested = new ListSet<ExecutionPlan>();
this.steps = new ExecutionSequence(this);
}
public ExecutionSequence getSequence() {
return steps;
}
public abstract boolean isRoot();
public abstract void setCacheable(boolean v);
public abstract boolean isCacheable();
public ListSet<ExecutionPlan> getNestedPlans() {
return nested;
}
public void addNestedPlan(ExecutionPlan ep) {
nested.add(ep);
}
public ValueManager getValueManager() {
return values;
}
@Override
public void visitInExecutionOrder(UnaryProcedure<HasPlanning> proc) {
steps.visitInExecutionOrder(proc);
}
@Override
public void visitInTestVerificationOrder(UnaryProcedure<HasPlanning> proc) {
steps.visitInTestVerificationOrder(proc);
}
@Override
public void prepareForCache() {
steps.prepareForCache();
}
@Override
public ExecutionType getExecutionType() {
return null;
}
// the plan is represented by a tree - of which the sequence is the root.
// convert the tree such that the last execution step is the single query step
// and all others are dependent steps.
protected static QueryStepOperation collapseOperationList(List<QueryStepOperation> ops) {
if (ops.isEmpty())
throw new SchemaException(Pass.PLANNER, "Invalid collapse");
QueryStepOperation end = ops.remove(ops.size() - 1);
for(QueryStepOperation qs : ops)
end.addRequirement(qs);
return end;
}
@Override
public void schedule(ExecutionPlanOptions opts, List<QueryStepOperation> qsteps, ProjectionInfo projection, SchemaContext sc,
ConnectionValuesMap cv, ExecutionPlan currentPlan)
throws PEException {
ArrayList<QueryStepOperation> buf = new ArrayList<QueryStepOperation>();
steps.schedule(opts, buf,projection, sc, cv, this);
if (buf.isEmpty()) return;
qsteps.add(collapseOperationList(buf));
}
@Override
public void display(SchemaContext sc, ConnectionValuesMap cv, ExecutionPlan currentPlan,
List<String> buf, String indent, EmitOptions opts) {
steps.display(sc, cv, this, buf," ",(opts == null ? EmitOptions.NONE.addMultilinePretty(" ") : opts.addMultilinePretty(" ")));
}
@Override
public Long getlastInsertId(ValueManager vm, SchemaContext sc, ConnectionValues cv) {
return steps.getlastInsertId(vm, sc, cv);
}
@Override
public Long getUpdateCount(SchemaContext sc, ConnectionValues cv) {
return steps.getUpdateCount(sc, cv);
}
@Override
public boolean useRowCount() {
return steps.useRowCount();
}
@Override
public void explain(SchemaContext sc, ConnectionValuesMap cv, ExecutionPlan currentPlan, List<ResultRow> rows,
ExplainOptions opts) {
steps.explain(sc, cv, this, rows, opts);
}
@Override
public CacheInvalidationRecord getCacheInvalidation(final SchemaContext sc) {
return steps.getCacheInvalidation(sc);
}
protected static final String[] basicExplainColumns =
new String[] { "Type", "Target_group", "Target_table", "Target_dist", "Target_index_hints", "Other", "SQL" };
public static void addExplainColumnHeaders(ColumnSet cs) {
for(String s : basicExplainColumns)
cs.addColumn(s,255,"varchar",Types.VARCHAR);
}
private IntermediateResultSet generateExplain(SchemaContext sc, ConnectionValuesMap cv, ExplainOptions opts) {
ColumnSet cs = new ColumnSet();
addExplainColumnHeaders(cs);
if (opts.isStatistics())
StepExecutionStatistics.addColumnHeaders(cs);
List<ResultRow> rows = new ArrayList<ResultRow>();
explain(sc,cv, this, rows,opts);
return new IntermediateResultSet(cs,rows);
}
public DDLQueryExecutionStep generateExplain(SchemaContext sc, ConnectionValuesMap cv, Statement sp, String origSQL) {
boolean standard = true;
if (sp.getExplain() == null)
standard = true;
else {
standard = !sp.getExplain().isRaw();
}
if (standard)
return new ExplainExecutionStep("explain", this, generateExplain(sc,cv,sp.getExplain()));
else {
return new ExplainExecutionStep("rawexplain",this, ExecToRawConverter.convertForRawExplain(sc, this, sp, origSQL));
}
}
public void display(SchemaContext sc, ConnectionValuesMap cv, PrintStream ps, EmitOptions opts) {
display(sc, cv, ps,"",opts);
}
public void display(SchemaContext sc, ConnectionValuesMap cv, PrintStream ps, String cntxt, EmitOptions opts) {
ps.println();
boolean prepared = values.getNumberOfParameters() > 0 && !values.hasPassDownParams();
ps.println("--- Execution Plan " + cntxt + (prepared ? " (prepared stmt) " : "") + " ---");
ArrayList<String> buf = new ArrayList<String>();
steps.display(sc, cv, this, buf," ",(opts == null ? EmitOptions.NONE.addMultilinePretty(" ") : opts.addMultilinePretty(" ")));
for(String s : buf)
ps.println(s);
ps.println("--- End Execution Plan ---");
}
public void logPlan(SchemaContext sc, ConnectionValuesMap cv, String cntxt, EmitOptions opts) {
if (!logger.isInfoEnabled())
return;
ByteArrayOutputStream buf = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(buf);
display(sc, cv,ps,cntxt,opts);
ps.flush();
ps.close();
logger.info(buf.toString());
}
public void traverseExecutionPlans(UnaryProcedure<ExecutionPlan> proc) {
// visit self
proc.execute(this);
// and now my nested plans
for(ExecutionPlan ep : getNestedPlans()) {
ep.traverseExecutionPlans(proc);
}
}
// should only be called on root exec plans
public abstract List<QueryStepOperation>
schedule(ExecutionPlanOptions opts, SSConnection connection, SchemaContext sc, ConnectionValuesMap cv) throws PEException;
}