private void removeUnusedAssigns(
      Mutable<ILogicalOperator> opRef, Set<LogicalVariable> toRemove, IOptimizationContext context)
      throws AlgebricksException {
    AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue();
    while (removeFromAssigns(op, toRemove, context) == 0) {
      if (op.getOperatorTag() == LogicalOperatorTag.AGGREGATE) {
        break;
      }
      op = (AbstractLogicalOperator) op.getInputs().get(0).getValue();
      opRef.setValue(op);
    }
    Iterator<Mutable<ILogicalOperator>> childIter = op.getInputs().iterator();
    while (childIter.hasNext()) {
      Mutable<ILogicalOperator> cRef = childIter.next();
      removeUnusedAssigns(cRef, toRemove, context);
    }
    if (op.hasNestedPlans()) {
      AbstractOperatorWithNestedPlans opWithNest = (AbstractOperatorWithNestedPlans) op;
      Iterator<ILogicalPlan> planIter = opWithNest.getNestedPlans().iterator();
      while (planIter.hasNext()) {
        ILogicalPlan p = planIter.next();
        for (Mutable<ILogicalOperator> r : p.getRoots()) {
          removeUnusedAssigns(r, toRemove, context);
        }
      }

      // Removes redundant nested plans that produces nothing
      for (int i = opWithNest.getNestedPlans().size() - 1; i >= 0; i--) {
        ILogicalPlan nestedPlan = opWithNest.getNestedPlans().get(i);
        List<Mutable<ILogicalOperator>> rootsToBeRemoved =
            new ArrayList<Mutable<ILogicalOperator>>();
        for (Mutable<ILogicalOperator> r : nestedPlan.getRoots()) {
          ILogicalOperator topOp = r.getValue();
          Set<LogicalVariable> producedVars = new ListSet<LogicalVariable>();
          VariableUtilities.getProducedVariablesInDescendantsAndSelf(topOp, producedVars);
          if (producedVars.size() == 0) {
            rootsToBeRemoved.add(r);
          }
        }
        // Makes sure the operator should have at least ONE nested plan even it is empty
        // (because a lot of places uses this assumption,  TODO(yingyib): clean them up).
        if (nestedPlan.getRoots().size() == rootsToBeRemoved.size()
            && opWithNest.getNestedPlans().size() > 1) {
          nestedPlan.getRoots().removeAll(rootsToBeRemoved);
          opWithNest.getNestedPlans().remove(nestedPlan);
        }
      }
    }
  }
 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;
  }