@Override
 public Void visitSubplanOperator(SubplanOperator op, Void arg) throws AlgebricksException {
   for (Mutable<ILogicalOperator> c : op.getInputs()) {
     VariableUtilities.getLiveVariables(c.getValue(), schemaVariables);
   }
   for (ILogicalPlan p : op.getNestedPlans()) {
     for (Mutable<ILogicalOperator> r : p.getRoots()) {
       VariableUtilities.getLiveVariables(r.getValue(), schemaVariables);
     }
   }
   return null;
 }
  @Override
  public Void visitLeftOuterJoinOperator(LeftOuterJoinOperator op, IOptimizationContext ctx)
      throws AlgebricksException {
    Map<LogicalVariable, EquivalenceClass> equivalenceClasses =
        new HashMap<LogicalVariable, EquivalenceClass>();
    List<FunctionalDependency> functionalDependencies = new ArrayList<FunctionalDependency>();
    ctx.putEquivalenceClassMap(op, equivalenceClasses);
    ctx.putFDList(op, functionalDependencies);
    ILogicalOperator opLeft = op.getInputs().get(0).getOperator();
    ILogicalOperator opRight = op.getInputs().get(1).getOperator();
    functionalDependencies.addAll(getOrComputeFDs(opLeft, ctx));
    functionalDependencies.addAll(getOrComputeFDs(opRight, ctx));
    equivalenceClasses.putAll(getOrComputeEqClasses(opLeft, ctx));
    equivalenceClasses.putAll(getOrComputeEqClasses(opRight, ctx));

    Collection<LogicalVariable> leftSideVars;
    if (opLeft.getSchema() == null) {
      leftSideVars = new LinkedList<LogicalVariable>();
      VariableUtilities.getLiveVariables(opLeft, leftSideVars);
      // actually, not all produced vars. are visible (due to projection)
      // so using cached schema is better and faster
    } else {
      leftSideVars = opLeft.getSchema();
    }
    ILogicalExpression expr = op.getCondition().getExpression();
    expr.getConstraintsForOuterJoin(functionalDependencies, leftSideVars);
    return null;
  }
 @Override
 public Void visitDistinctOperator(DistinctOperator op, Void arg) throws AlgebricksException {
   List<LogicalVariable> allLiveVars = new ArrayList<LogicalVariable>();
   for (Mutable<ILogicalOperator> c : op.getInputs()) {
     VariableUtilities.getLiveVariables(c.getValue(), allLiveVars);
   }
   VariableUtilities.getProducedVariables(op, allLiveVars);
   /** put distinct vars first */
   schemaVariables.addAll(op.getDistinctByVarList());
   /** then other live vars */
   for (LogicalVariable var : allLiveVars) {
     if (!schemaVariables.contains(var)) {
       schemaVariables.add(var);
     }
   }
   return null;
 }
 @Override
 public Void visitUnnestMapOperator(UnnestMapOperator op, Void arg) throws AlgebricksException {
   if (op.propagatesInput()) {
     standardLayout(op);
   } else {
     VariableUtilities.getProducedVariables(op, schemaVariables);
   }
   return null;
 }
  private void fdsEqClassesForAbstractUnnestOperator(
      AbstractUnnestOperator op, IOptimizationContext ctx) throws AlgebricksException {
    ILogicalOperator inp1 = op.getInputs().get(0).getOperator();
    Map<LogicalVariable, EquivalenceClass> eqClasses = getOrComputeEqClasses(inp1, ctx);
    ctx.putEquivalenceClassMap(op, eqClasses);
    List<FunctionalDependency> fds = getOrComputeFDs(inp1, ctx);
    ctx.putFDList(op, fds);

    ILogicalExpression expr = op.getExpressionRef().getExpression();
    if (expr.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) {
      AbstractFunctionCallExpression afe = (AbstractFunctionCallExpression) expr;
      if (afe.getKind() == FunctionKind.UNNEST
          && ((UnnestingFunctionCallExpression) afe).returnsUniqueValues()) {
        List<LogicalVariable> vars = new ArrayList<LogicalVariable>();
        VariableUtilities.getLiveVariables(op, vars);
        ArrayList<LogicalVariable> h = new ArrayList<LogicalVariable>();
        h.addAll(op.getVariables());
        FunctionalDependency fd = new FunctionalDependency(h, vars);
        fds.add(fd);
      }
    }
  }
 @Override
 public Void visitGroupByOperator(GroupByOperator op, Void arg) throws AlgebricksException {
   for (ILogicalPlan p : op.getNestedPlans()) {
     for (Mutable<ILogicalOperator> r : p.getRoots()) {
       VariableUtilities.getLiveVariables(r.getValue(), schemaVariables);
     }
   }
   for (Pair<LogicalVariable, Mutable<ILogicalExpression>> p : op.getGroupByList()) {
     if (p.first != null) {
       schemaVariables.add(p.first);
     }
   }
   for (Pair<LogicalVariable, Mutable<ILogicalExpression>> p : op.getDecorList()) {
     if (p.first != null) {
       schemaVariables.add(p.first);
     } else {
       ILogicalExpression e = p.second.getValue();
       if (e.getExpressionTag() == LogicalExpressionTag.VARIABLE) {
         schemaVariables.add(((VariableReferenceExpression) e).getVariableReference());
       }
     }
   }
   return null;
 }
 @Override
 public Void visitAssignOperator(AssignOperator op, IOptimizationContext ctx)
     throws AlgebricksException {
   ILogicalOperator inp1 = op.getInputs().get(0).getOperator();
   Map<LogicalVariable, EquivalenceClass> eqClasses = getOrComputeEqClasses(inp1, ctx);
   ctx.putEquivalenceClassMap(op, eqClasses);
   List<LogicalVariable> used = new ArrayList<LogicalVariable>();
   VariableUtilities.getUsedVariables(op, used);
   List<FunctionalDependency> fds1 = getOrComputeFDs(inp1, ctx);
   List<FunctionalDependency> eFds = new ArrayList<FunctionalDependency>(fds1.size());
   for (FunctionalDependency fd : fds1) {
     if (fd.getTail().containsAll(used)) {
       List<LogicalVariable> hd = new ArrayList<LogicalVariable>(fd.getHead());
       List<LogicalVariable> tl = new ArrayList<LogicalVariable>(fd.getTail());
       tl.addAll(op.getVariables());
       FunctionalDependency fd2 = new FunctionalDependency(hd, tl);
       eFds.add(fd2);
     } else {
       eFds.add(fd);
     }
   }
   ctx.putFDList(op, eFds);
   return null;
 }
 private void standardLayout(ILogicalOperator op) throws AlgebricksException {
   for (Mutable<ILogicalOperator> c : op.getInputs()) {
     VariableUtilities.getLiveVariables(c.getValue(), schemaVariables);
   }
   VariableUtilities.getProducedVariables(op, schemaVariables);
 }
 @Override
 public Void visitIntersectOperator(IntersectOperator op, Void arg) throws AlgebricksException {
   VariableUtilities.getProducedVariables(op, schemaVariables);
   return null;
 }
 @Override
 public Void visitNestedTupleSourceOperator(NestedTupleSourceOperator op, Void arg)
     throws AlgebricksException {
   VariableUtilities.getLiveVariables(op.getSourceOperator(), schemaVariables);
   return null;
 }