@Override
 public Void visitAssignOperator(AssignOperator op, Pair<LogicalVariable, LogicalVariable> pair)
     throws AlgebricksException {
   List<LogicalVariable> variables = op.getVariables();
   int n = variables.size();
   for (int i = 0; i < n; i++) {
     if (variables.get(i).equals(pair.first)) {
       variables.set(i, pair.second);
     } else {
       op.getExpressions().get(i).getValue().substituteVar(pair.first, pair.second);
     }
   }
   // Substitute variables stored in ordering property
   if (op.getExplicitOrderingProperty() != null) {
     List<OrderColumn> orderColumns = op.getExplicitOrderingProperty().getOrderColumns();
     for (int i = 0; i < orderColumns.size(); i++) {
       OrderColumn oc = orderColumns.get(i);
       if (oc.getColumn().equals(pair.first)) {
         orderColumns.set(i, new OrderColumn(pair.second, oc.getOrder()));
       }
     }
   }
   substVarTypes(op, pair);
   return null;
 }
 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;
 }
  @Override
  public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
      throws AlgebricksException {

    if (!opRef.getValue().getOperatorTag().equals(LogicalOperatorTag.ASSIGN)) {
      return false;
    }

    AssignOperator assignUnion = (AssignOperator) opRef.getValue();

    if (assignUnion.getExpressions().get(0).getValue().getExpressionTag()
        != LogicalExpressionTag.FUNCTION_CALL) return false;

    AbstractFunctionCallExpression u =
        (AbstractFunctionCallExpression) assignUnion.getExpressions().get(0).getValue();
    if (!AsterixBuiltinFunctions.UNION.equals(u.getFunctionIdentifier())) {
      return false;
    }

    // Retrieving the logical variables for the union from the two aggregates which are inputs to
    // the join
    Mutable<ILogicalOperator> join = assignUnion.getInputs().get(0);

    LogicalOperatorTag tag1 = join.getValue().getOperatorTag();
    if (tag1 != LogicalOperatorTag.INNERJOIN && tag1 != LogicalOperatorTag.LEFTOUTERJOIN) {
      return false;
    }
    AbstractBinaryJoinOperator join1 = (AbstractBinaryJoinOperator) join.getValue();
    ILogicalExpression cond1 = join1.getCondition().getValue();
    // don't try to push a product down
    if (!OperatorPropertiesUtil.isAlwaysTrueCond(cond1)) {
      return false;
    }

    List<Mutable<ILogicalOperator>> joinInputs = join.getValue().getInputs();

    Mutable<ILogicalOperator> left_branch = joinInputs.get(0);
    Mutable<ILogicalOperator> right_branch = joinInputs.get(1);

    List<LogicalVariable> input1Var = new ArrayList<LogicalVariable>();
    VariableUtilities.getProducedVariables(left_branch.getValue(), input1Var);

    List<LogicalVariable> input2Var = new ArrayList<LogicalVariable>();
    VariableUtilities.getProducedVariables(right_branch.getValue(), input2Var);

    List<Triple<LogicalVariable, LogicalVariable, LogicalVariable>> varMap =
        new ArrayList<Triple<LogicalVariable, LogicalVariable, LogicalVariable>>(1);
    Triple<LogicalVariable, LogicalVariable, LogicalVariable> triple =
        new Triple<LogicalVariable, LogicalVariable, LogicalVariable>(
            input1Var.get(0), input2Var.get(0), assignUnion.getVariables().get(0));
    varMap.add(triple);
    UnionAllOperator unionOp = new UnionAllOperator(varMap);

    unionOp.getInputs().add(left_branch);
    unionOp.getInputs().add(right_branch);

    context.computeAndSetTypeEnvironmentForOperator(unionOp);

    opRef.setValue(unionOp);

    return true;
  }