blob: 641b640fdcccd4128c88ccc644038f7b112fd2f6 [file] [log] [blame]
package edu.uci.ics.asterix.optimizer.rules;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.mutable.Mutable;
import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
import edu.uci.ics.hyracks.algebricks.common.utils.Pair;
import edu.uci.ics.hyracks.algebricks.core.algebra.base.ILogicalExpression;
import edu.uci.ics.hyracks.algebricks.core.algebra.base.ILogicalOperator;
import edu.uci.ics.hyracks.algebricks.core.algebra.base.ILogicalPlan;
import edu.uci.ics.hyracks.algebricks.core.algebra.base.IOptimizationContext;
import edu.uci.ics.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
import edu.uci.ics.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
import edu.uci.ics.hyracks.algebricks.core.algebra.base.LogicalVariable;
import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.AbstractBinaryJoinOperator;
import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.GroupByOperator;
import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
import edu.uci.ics.hyracks.algebricks.core.algebra.util.OperatorManipulationUtil;
import edu.uci.ics.hyracks.algebricks.core.algebra.util.OperatorPropertiesUtil;
import edu.uci.ics.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule;
public class PushGroupByThroughProduct implements IAlgebraicRewriteRule {
private enum PushTestResult {
FALSE, TRUE, REPEATED_DECORS
}
@Override
public boolean rewritePre(Mutable<ILogicalOperator> opRef, IOptimizationContext context) throws AlgebricksException {
return false;
}
@Override
public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context) throws AlgebricksException {
AbstractLogicalOperator op1 = (AbstractLogicalOperator) opRef.getValue();
if (op1.getOperatorTag() != LogicalOperatorTag.GROUP) {
return false;
}
Mutable<ILogicalOperator> opRef2 = op1.getInputs().get(0);
AbstractLogicalOperator op2 = (AbstractLogicalOperator) opRef2.getValue();
if (op2.getOperatorTag() != LogicalOperatorTag.INNERJOIN) {
return false;
}
InnerJoinOperator join = (InnerJoinOperator) op2;
if (!OperatorPropertiesUtil.isAlwaysTrueCond(join.getCondition().getValue())) {
// not a product
return false;
}
GroupByOperator gby = (GroupByOperator) op1;
List<Pair<LogicalVariable, Mutable<ILogicalExpression>>> decorToPush = new ArrayList<Pair<LogicalVariable, Mutable<ILogicalExpression>>>();
List<Pair<LogicalVariable, Mutable<ILogicalExpression>>> decorNotToPush = new ArrayList<Pair<LogicalVariable, Mutable<ILogicalExpression>>>();
Mutable<ILogicalOperator> opLeftRef = join.getInputs().get(0);
ILogicalOperator opLeft = opLeftRef.getValue();
switch (canPushThrough(gby, opLeft, decorToPush, decorNotToPush)) {
case REPEATED_DECORS: {
return false;
}
case TRUE: {
push(opRef, opRef2, 0, decorToPush, decorNotToPush, context);
return true;
}
case FALSE: {
decorToPush.clear();
Mutable<ILogicalOperator> opRightRef = join.getInputs().get(1);
ILogicalOperator opRight = opRightRef.getValue();
if (canPushThrough(gby, opRight, decorToPush, decorNotToPush) == PushTestResult.TRUE) {
push(opRef, opRef2, 1, decorToPush, decorNotToPush, context);
return true;
} else {
return false;
}
}
default: {
throw new IllegalStateException();
}
}
}
private void push(Mutable<ILogicalOperator> opRefGby, Mutable<ILogicalOperator> opRefJoin, int branch,
List<Pair<LogicalVariable, Mutable<ILogicalExpression>>> decorToPush,
List<Pair<LogicalVariable, Mutable<ILogicalExpression>>> decorNotToPush, IOptimizationContext context)
throws AlgebricksException {
GroupByOperator gby = (GroupByOperator) opRefGby.getValue();
AbstractBinaryJoinOperator join = (AbstractBinaryJoinOperator) opRefJoin.getValue();
gby.getDecorList().clear();
gby.getDecorList().addAll(decorToPush);
for (Pair<LogicalVariable, Mutable<ILogicalExpression>> p : decorNotToPush) {
LogicalVariable v1 = p.first;
VariableReferenceExpression varRef = (VariableReferenceExpression) p.second.getValue();
LogicalVariable v2 = varRef.getVariableReference();
OperatorManipulationUtil.substituteVarRec(join, v2, v1, true, context);
}
Mutable<ILogicalOperator> branchRef = join.getInputs().get(branch);
ILogicalOperator opBranch = branchRef.getValue();
opRefJoin.setValue(opBranch);
branchRef.setValue(gby);
opRefGby.setValue(join);
}
private PushTestResult canPushThrough(GroupByOperator gby, ILogicalOperator branch,
List<Pair<LogicalVariable, Mutable<ILogicalExpression>>> toPush,
List<Pair<LogicalVariable, Mutable<ILogicalExpression>>> notToPush) throws AlgebricksException {
Collection<LogicalVariable> fromBranch = new HashSet<LogicalVariable>();
VariableUtilities.getLiveVariables(branch, fromBranch);
Collection<LogicalVariable> usedInGbyExprList = new ArrayList<LogicalVariable>();
for (Pair<LogicalVariable, Mutable<ILogicalExpression>> p : gby.getGroupByList()) {
p.second.getValue().getUsedVariables(usedInGbyExprList);
}
if (!fromBranch.containsAll(usedInGbyExprList)) {
return PushTestResult.FALSE;
}
Set<LogicalVariable> free = new HashSet<LogicalVariable>();
for (ILogicalPlan p : gby.getNestedPlans()) {
for (Mutable<ILogicalOperator> r : p.getRoots()) {
OperatorPropertiesUtil.getFreeVariablesInSelfOrDesc((AbstractLogicalOperator) r.getValue(), free);
}
}
if (!fromBranch.containsAll(free)) {
return PushTestResult.FALSE;
}
Set<LogicalVariable> decorVarRhs = new HashSet<LogicalVariable>();
decorVarRhs.clear();
for (Pair<LogicalVariable, Mutable<ILogicalExpression>> p : gby.getDecorList()) {
ILogicalExpression expr = p.second.getValue();
if (expr.getExpressionTag() != LogicalExpressionTag.VARIABLE) {
return PushTestResult.FALSE;
}
VariableReferenceExpression varRef = (VariableReferenceExpression) expr;
LogicalVariable v = varRef.getVariableReference();
if (decorVarRhs.contains(v)) {
return PushTestResult.REPEATED_DECORS;
}
decorVarRhs.add(v);
if (fromBranch.contains(v)) {
toPush.add(p);
} else {
notToPush.add(p);
}
}
return PushTestResult.TRUE;
}
}