public Frame decorrelateRel(HiveAggregate rel) throws SemanticException {
    //
    // Rewrite logic:
    //
    // 1. Permute the group by keys to the front.
    // 2. If the input of an aggregate produces correlated variables,
    //    add them to the group list.
    // 3. Change aggCalls to reference the new project.
    //

    // Aggregate itself should not reference cor vars.
    assert !cm.mapRefRelToCorRef.containsKey(rel);

    final RelNode oldInput = rel.getInput();
    final Frame frame = getInvoke(oldInput, rel);
    if (frame == null) {
      // If input has not been rewritten, do not rewrite this rel.
      return null;
    }
    //assert !frame.corVarOutputPos.isEmpty();
    final RelNode newInput = frame.r;

    // map from newInput
    Map<Integer, Integer> mapNewInputToProjOutputs =  new HashMap<>();
    final int oldGroupKeyCount = rel.getGroupSet().cardinality();

    // Project projects the original expressions,
    // plus any correlated variables the input wants to pass along.
    final List<Pair<RexNode, String>> projects = Lists.newArrayList();

    List<RelDataTypeField> newInputOutput =
        newInput.getRowType().getFieldList();

    int newPos = 0;

    // oldInput has the original group by keys in the front.
    final NavigableMap<Integer, RexLiteral> omittedConstants = new TreeMap<>();
    for (int i = 0; i < oldGroupKeyCount; i++) {
      final RexLiteral constant = projectedLiteral(newInput, i);
      int newInputPos = frame.oldToNewOutputs.get(i);
      projects.add(RexInputRef.of2(newInputPos, newInputOutput));
      mapNewInputToProjOutputs.put(newInputPos, newPos);
      newPos++;
    }

    final SortedMap<CorDef, Integer> corDefOutputs = new TreeMap<>();
    if (!frame.corDefOutputs.isEmpty()) {
      // If input produces correlated variables, move them to the front,
      // right after any existing GROUP BY fields.

      // Now add the corVars from the input, starting from
      // position oldGroupKeyCount.
      for (Map.Entry<CorDef, Integer> entry
          : frame.corDefOutputs.entrySet()) {
        projects.add(RexInputRef.of2(entry.getValue(), newInputOutput));

        corDefOutputs.put(entry.getKey(), newPos);
        mapNewInputToProjOutputs.put(entry.getValue(), newPos);
        newPos++;
      }
    }

    // add the remaining fields
    final int newGroupKeyCount = newPos;
    for (int i = 0; i < newInputOutput.size(); i++) {
      if (!mapNewInputToProjOutputs.containsKey(i)) {
        projects.add(RexInputRef.of2(i, newInputOutput));
        mapNewInputToProjOutputs.put(i, newPos);
        newPos++;
      }
    }

    assert newPos == newInputOutput.size();

    // This Project will be what the old input maps to,
    // replacing any previous mapping from old input).
    RelNode newProject = HiveProject.create(newInput, Pair.left(projects), Pair.right(projects));

    // update mappings:
    // oldInput ----> newInput
    //
    //                newProject
    //                   |
    // oldInput ----> newInput
    //
    // is transformed to
    //
    // oldInput ----> newProject
    //                   |
    //                newInput
    Map<Integer, Integer> combinedMap = Maps.newHashMap();

    for (Integer oldInputPos : frame.oldToNewOutputs.keySet()) {
      combinedMap.put(oldInputPos,
          mapNewInputToProjOutputs.get(
              frame.oldToNewOutputs.get(oldInputPos)));
    }

    register(oldInput, newProject, combinedMap, corDefOutputs);

    // now it's time to rewrite the Aggregate
    final ImmutableBitSet newGroupSet = ImmutableBitSet.range(newGroupKeyCount);
    List<AggregateCall> newAggCalls = Lists.newArrayList();
    List<AggregateCall> oldAggCalls = rel.getAggCallList();

    int oldInputOutputFieldCount = rel.getGroupSet().cardinality();
    int newInputOutputFieldCount = newGroupSet.cardinality();

    int i = -1;
    for (AggregateCall oldAggCall : oldAggCalls) {
      ++i;
      List<Integer> oldAggArgs = oldAggCall.getArgList();

      List<Integer> aggArgs = Lists.newArrayList();

      // Adjust the aggregator argument positions.
      // Note aggregator does not change input ordering, so the input
      // output position mapping can be used to derive the new positions
      // for the argument.
      for (int oldPos : oldAggArgs) {
        aggArgs.add(combinedMap.get(oldPos));
      }
      final int filterArg = oldAggCall.filterArg < 0 ? oldAggCall.filterArg
          : combinedMap.get(oldAggCall.filterArg);

      newAggCalls.add(
          oldAggCall.adaptTo(newProject, aggArgs, filterArg,
              oldGroupKeyCount, newGroupKeyCount));

      // The old to new output position mapping will be the same as that
      // of newProject, plus any aggregates that the oldAgg produces.
      combinedMap.put(
          oldInputOutputFieldCount + i,
          newInputOutputFieldCount + i);
    }

    relBuilder.push(
        new HiveAggregate(rel.getCluster(), rel.getTraitSet(), newProject,
            newGroupSet, null, newAggCalls));

    if (!omittedConstants.isEmpty()) {
      final List<RexNode> postProjects = new ArrayList<>(relBuilder.fields());
      for (Map.Entry<Integer, RexLiteral> entry
          : omittedConstants.descendingMap().entrySet()) {
        postProjects.add(entry.getKey() + frame.corDefOutputs.size(),
            entry.getValue());
      }
      relBuilder.project(postProjects);
    }

    // Aggregate does not change input ordering so corVars will be
    // located at the same position as the input newProject.
    return register(rel, relBuilder.build(), combinedMap, corDefOutputs);
  }
Returns a literal output field, or null if it is not literal.
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveRelDecorrelator.java