@Override
    public PhysicalOperator visitCollapsingAggregate(CollapsingAggregate agg, Object value)
        throws OptimizerException {

      if (!(agg.getInput() instanceof Segment)) {
        throw new OptimizerException(
            String.format(
                "Currently, Drill only supports CollapsingAggregate immediately preceded by a Segment.  The input of this operator is %s.",
                agg.getInput()));
      }
      Segment segment = (Segment) agg.getInput();

      if (!agg.getWithin().equals(segment.getName())) {
        throw new OptimizerException(
            String.format(
                "Currently, Drill only supports CollapsingAggregate immediately preceded by a Segment where the CollapsingAggregate works on the defined segments.  In this case, the segment has been defined based on the name %s but the collapsing aggregate is working within the field %s.",
                segment.getName(), agg.getWithin()));
      }

      // a collapsing aggregate is a currently implemented as a sort followed by a streaming
      // aggregate.
      List<OrderDef> orderDefs = Lists.newArrayList();

      List<NamedExpression> keys = Lists.newArrayList();
      for (LogicalExpression e : segment.getExprs()) {
        if (!(e instanceof SchemaPath))
          throw new OptimizerException(
              "The basic optimizer doesn't currently support collapsing aggregate where the segment value is something other than a SchemaPath.");
        keys.add(new NamedExpression(e, new FieldReference((SchemaPath) e)));
        orderDefs.add(new OrderDef(Direction.ASC, e));
      }
      Sort sort = new Sort(segment.getInput().accept(this, value), orderDefs, false);

      StreamingAggregate sa =
          new StreamingAggregate(
              sort, keys.toArray(new NamedExpression[keys.size()]), agg.getAggregations(), 1.0f);
      return sa;
    }