private int removeFromAssigns(
     AbstractLogicalOperator op, Set<LogicalVariable> toRemove, IOptimizationContext context)
     throws AlgebricksException {
   switch (op.getOperatorTag()) {
     case ASSIGN:
       {
         AssignOperator assign = (AssignOperator) op;
         if (removeUnusedVarsAndExprs(toRemove, assign.getVariables(), assign.getExpressions())) {
           context.computeAndSetTypeEnvironmentForOperator(assign);
         }
         return assign.getVariables().size();
       }
     case AGGREGATE:
       {
         AggregateOperator agg = (AggregateOperator) op;
         if (removeUnusedVarsAndExprs(toRemove, agg.getVariables(), agg.getExpressions())) {
           context.computeAndSetTypeEnvironmentForOperator(agg);
         }
         return agg.getVariables().size();
       }
     case UNNEST:
       {
         UnnestOperator uOp = (UnnestOperator) op;
         LogicalVariable pVar = uOp.getPositionalVariable();
         if (pVar != null && toRemove.contains(pVar)) {
           uOp.setPositionalVariable(null);
         }
         break;
       }
     case UNIONALL:
       {
         UnionAllOperator unionOp = (UnionAllOperator) op;
         if (removeUnusedVarsFromUnionAll(unionOp, toRemove)) {
           context.computeAndSetTypeEnvironmentForOperator(unionOp);
         }
         return unionOp.getVariableMappings().size();
       }
   }
   return -1;
 }
 private void collectUnusedAssignedVars(
     AbstractLogicalOperator op,
     Set<LogicalVariable> toRemove,
     boolean first,
     IOptimizationContext context)
     throws AlgebricksException {
   if (!first) {
     context.addToDontApplySet(this, op);
   }
   for (Mutable<ILogicalOperator> c : op.getInputs()) {
     collectUnusedAssignedVars((AbstractLogicalOperator) c.getValue(), toRemove, false, context);
   }
   if (op.hasNestedPlans()) {
     AbstractOperatorWithNestedPlans opWithNested = (AbstractOperatorWithNestedPlans) op;
     for (ILogicalPlan plan : opWithNested.getNestedPlans()) {
       for (Mutable<ILogicalOperator> r : plan.getRoots()) {
         collectUnusedAssignedVars(
             (AbstractLogicalOperator) r.getValue(), toRemove, false, context);
       }
     }
   }
   boolean removeUsedVars = true;
   switch (op.getOperatorTag()) {
     case ASSIGN:
       {
         AssignOperator assign = (AssignOperator) op;
         toRemove.addAll(assign.getVariables());
         break;
       }
     case AGGREGATE:
       {
         AggregateOperator agg = (AggregateOperator) op;
         toRemove.addAll(agg.getVariables());
         break;
       }
     case UNNEST:
       {
         UnnestOperator uOp = (UnnestOperator) op;
         LogicalVariable pVar = uOp.getPositionalVariable();
         if (pVar != null) {
           toRemove.add(pVar);
         }
         break;
       }
     case UNIONALL:
       {
         UnionAllOperator unionOp = (UnionAllOperator) op;
         for (Triple<LogicalVariable, LogicalVariable, LogicalVariable> varMapping :
             unionOp.getVariableMappings()) {
           toRemove.add(varMapping.third);
         }
         removeUsedVars = false;
         break;
       }
   }
   if (removeUsedVars) {
     List<LogicalVariable> used = new LinkedList<LogicalVariable>();
     VariableUtilities.getUsedVariables(op, used);
     toRemove.removeAll(used);
   }
 }
  @Override
  public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
      throws AlgebricksException {

    AbstractLogicalOperator unnest = (AbstractLogicalOperator) opRef.getValue();
    if (unnest.getOperatorTag() != LogicalOperatorTag.UNNEST) {
      return false;
    }
    UnnestOperator unnestOpRef = (UnnestOperator) opRef.getValue();
    Mutable<ILogicalOperator> unionOp = unnest.getInputs().get(0);

    AbstractLogicalOperator unionAbstractOp = (AbstractLogicalOperator) unionOp.getValue();
    if (unionAbstractOp.getOperatorTag() != LogicalOperatorTag.UNIONALL) {
      return false;
    }

    LogicalVariable unnestVar1 = context.newVar();
    UnnestOperator unnest1 =
        new UnnestOperator(
            unnestVar1,
            new MutableObject<ILogicalExpression>(
                unnestOpRef.getExpressionRef().getValue().cloneExpression()));
    LogicalVariable unnestVar2 = context.newVar();
    UnnestOperator unnest2 =
        new UnnestOperator(
            unnestVar2,
            new MutableObject<ILogicalExpression>(
                unnestOpRef.getExpressionRef().getValue().cloneExpression()));

    // Getting the two topmost branched and adding them as an input to the unnests:
    Mutable<ILogicalOperator> branch1 = unionAbstractOp.getInputs().get(0);
    ILogicalOperator agg1 = branch1.getValue();
    List<LogicalVariable> agg1_var = new ArrayList<LogicalVariable>();
    VariableUtilities.getLiveVariables(agg1, agg1_var);
    Mutable<ILogicalOperator> branch2 = unionAbstractOp.getInputs().get(1);
    ILogicalOperator agg2 = branch2.getValue();
    List<LogicalVariable> agg2_var = new ArrayList<LogicalVariable>();
    VariableUtilities.getLiveVariables(agg2, agg2_var);

    // Modifying the unnest so it has the right variable
    List<LogicalVariable> var_unnest_1 = new ArrayList<LogicalVariable>();
    unnest1.getExpressionRef().getValue().getUsedVariables(var_unnest_1);
    unnest1.getExpressionRef().getValue().substituteVar(var_unnest_1.get(0), agg1_var.get(0));

    List<LogicalVariable> var_unnest2 = new ArrayList<LogicalVariable>();
    unnest2.getExpressionRef().getValue().getUsedVariables(var_unnest2);
    unnest2.getExpressionRef().getValue().substituteVar(var_unnest2.get(0), agg2_var.get(0));

    unnest1.getInputs().add(branch1);
    unnest2.getInputs().add(branch2);
    context.computeAndSetTypeEnvironmentForOperator(unnest1);
    context.computeAndSetTypeEnvironmentForOperator(unnest2);

    // creating a new union operator with the updated logical variables
    List<Triple<LogicalVariable, LogicalVariable, LogicalVariable>> varMap =
        new ArrayList<Triple<LogicalVariable, LogicalVariable, LogicalVariable>>(1);
    Triple<LogicalVariable, LogicalVariable, LogicalVariable> union_triple_vars =
        new Triple<LogicalVariable, LogicalVariable, LogicalVariable>(
            unnestVar1, unnestVar2, unnestOpRef.getVariables().get(0));
    varMap.add(union_triple_vars);
    UnionAllOperator unionOpFinal = new UnionAllOperator(varMap);

    unionOpFinal.getInputs().add(new MutableObject<ILogicalOperator>(unnest1));
    unionOpFinal.getInputs().add(new MutableObject<ILogicalOperator>(unnest2));

    context.computeAndSetTypeEnvironmentForOperator(unionOpFinal);

    opRef.setValue(unionOpFinal);
    return true;
  }