package com.tesora.dve.sql.statement.dml.compound;
/*
* #%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.Collections;
import java.util.List;
import java.util.Set;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.sql.node.Edge;
import com.tesora.dve.sql.node.EdgeName;
import com.tesora.dve.sql.node.LanguageNode;
import com.tesora.dve.sql.node.MultiEdge;
import com.tesora.dve.sql.node.SingleEdge;
import com.tesora.dve.sql.node.expression.CaseExpression;
import com.tesora.dve.sql.node.expression.ExpressionNode;
import com.tesora.dve.sql.node.expression.LiteralExpression;
import com.tesora.dve.sql.node.expression.WhenClause;
import com.tesora.dve.sql.parser.SourceLocation;
import com.tesora.dve.sql.schema.SchemaContext;
import com.tesora.dve.sql.statement.Statement;
import com.tesora.dve.sql.statement.dml.AliasInformation;
import com.tesora.dve.sql.statement.dml.DMLStatement;
import com.tesora.dve.sql.statement.dml.SelectStatement;
import com.tesora.dve.sql.transform.behaviors.BehaviorConfiguration;
import com.tesora.dve.sql.transform.execution.ExecutionPlan;
import com.tesora.dve.sql.transform.execution.ExecutionSequence;
import com.tesora.dve.sql.transform.execution.HasPlanning;
import com.tesora.dve.sql.transform.execution.TriggerBranchExecutionStep;
import com.tesora.dve.sql.transform.strategy.FeaturePlannerIdentifier;
import com.tesora.dve.sql.transform.strategy.PlannerContext;
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.MultiFeatureStep;
public class CaseStatement extends CompoundStatement implements FeaturePlanner {
protected SingleEdge<CaseStatement, ExpressionNode> testExpression =
new SingleEdge<CaseStatement, ExpressionNode>(CaseStatement.class, this, EdgeName.CASE_STMT_TESTCLAUSE);
protected SingleEdge<CaseStatement, Statement> elseExpression =
new SingleEdge<CaseStatement, Statement>(CaseStatement.class, this, EdgeName.CASE_STMT_ELSECLAUSE);
protected MultiEdge<CaseStatement, StatementWhenClause> whenClauses =
new MultiEdge<CaseStatement, StatementWhenClause>(CaseStatement.class, this, EdgeName.CASE_STMT_WHENCLAUSE);
@SuppressWarnings("rawtypes")
protected List edges = new ArrayList();
public CaseStatement(SourceLocation location, ExpressionNode testExpression,
List<StatementWhenClause> whenClauses,
Statement elseExpression) {
super(location);
this.testExpression.set(testExpression);
this.elseExpression.set(elseExpression);
this.whenClauses.set(whenClauses);
edges.add(this.testExpression);
edges.add(this.whenClauses);
edges.add(this.elseExpression);
}
public ExpressionNode getTestExpression() {
return testExpression.get();
}
public Statement getElseResult() {
return elseExpression.get();
}
public List<StatementWhenClause> getWhenClauses(){
return whenClauses.getMulti();
}
public MultiEdge<?,StatementWhenClause> getWhenClausesEdge() {
return whenClauses;
}
@Override
public void normalize(SchemaContext sc) {
// TODO Auto-generated method stub
}
@Override
protected boolean schemaSelfEqual(LanguageNode other) {
// TODO Auto-generated method stub
return false;
}
@Override
protected int selfHashCode() {
// TODO Auto-generated method stub
return 0;
}
@Override
public <T extends Edge<?,?>> List<T> getEdges() {
return edges;
}
/**
* We need to select the code path to follow. Here we rewrite the original
* CASE statement as a CaseExpression which evaluates the CASE condition and
* returns the index of the evaluated branch.
* We then use this index to choose the appropriate statement to execute.
* Branches are indexed starting from 1. The else branch has index 0.
*/
@Override
public FeatureStep plan(final SchemaContext sc, final BehaviorConfiguration config)
throws PEException {
final int numCases = this.whenClauses.size() + 1;
final SelectStatement caseEvaluationStmt = new SelectStatement(new AliasInformation());
final List<Statement> branchStmts = new ArrayList<Statement>(numCases);
final List<WhenClause> whens = new ArrayList<WhenClause>(numCases);
branchStmts.add(this.elseExpression.get()); // ELSE branch must be at index 0.
// Now append the WHEN branches indexing from 1.
long branchCounter = 1;
for (final StatementWhenClause when : this.whenClauses) {
whens.add(buildLiteralWhenClause(when.getTestExpression(), branchCounter++));
branchStmts.add(when.getResultStatement());
}
caseEvaluationStmt.setProjection(Collections.<ExpressionNode> singletonList(new CaseExpression(this.testExpression.get(), LiteralExpression
.makeLongLiteral(0), whens,
null)));
final FeatureStep plan = new MultiFeatureStep(this) {
/**
* CASE evaluation statement is the first child followed by
* statements for individual branches.
*/
@Override
public void schedule(PlannerContext pc, ExecutionSequence es, Set<FeatureStep> scheduled) throws PEException {
if (!scheduled.add(this)) {
return;
}
schedulePrefix(pc, es, scheduled);
final ExecutionPlan parentPlan = es.getPlan();
final HasPlanning caseEvaluationStep = ExecutionSequence.buildSubSequence(pc, getSelfChildren().get(0), parentPlan); // CASE branch evaluation
final List<HasPlanning> branchOperationSteps = new ArrayList<HasPlanning>(numCases);
final int numChildren = getSelfChildren().size();
for (int i = 1; i < numChildren; i++) {
branchOperationSteps.add(ExecutionSequence.buildSubSequence(pc, getSelfChildren().get(i), parentPlan));
}
es.append(new TriggerBranchExecutionStep(getDatabase(pc), getStorageGroup(sc),
caseEvaluationStep, branchOperationSteps));
}
}.withDefangInvariants();
plan.addChild(caseEvaluationStmt.plan(sc, config));
for (final Statement branchStmt : branchStmts) {
plan.addChild(branchStmt.plan(sc, config));
}
return plan;
}
private WhenClause buildLiteralWhenClause(final ExpressionNode branchTest, final long branchIndex) {
return new WhenClause(branchTest, LiteralExpression.makeLongLiteral(branchIndex), null);
}
@Override
public boolean emitting() {
// TODO Auto-generated method stub
return false;
}
@Override
public void emit(String what) {
// TODO Auto-generated method stub
}
@Override
public FeatureStep plan(DMLStatement stmt, PlannerContext context) throws PEException {
throw new PEException("Illegal call to CompoundStatement.plan");
}
@Override
public FeaturePlannerIdentifier getFeaturePlannerID() {
// TODO Auto-generated method stub
return null;
}
}