@Override
  public void onMatch(RelOptRuleCall call) {
    Project project = call.rel(0);
    Aggregate aggregate = call.rel(1);

    // 1. We go over the expressions in the project operator
    //    and we separate the windowing nodes that are result
    //    of an aggregate expression from the rest of nodes
    final int groupingFields = aggregate.getGroupCount() + aggregate.getIndicatorCount();
    Set<String> projectExprsDigest = new HashSet<String>();
    Map<String, RexNode> windowingExprsDigestToNodes = new HashMap<String,RexNode>();
    for (RexNode r : project.getProjects()) {
      if (r instanceof RexOver) {
        RexOver rexOverNode = (RexOver) r;
        // Operands
        for (RexNode operand : rexOverNode.getOperands()) {
          if (operand instanceof RexInputRef &&
                  ((RexInputRef)operand).getIndex() >= groupingFields) {
            windowingExprsDigestToNodes.put(operand.toString(), operand);
          }
        }
        // Partition keys
        for (RexNode partitionKey : rexOverNode.getWindow().partitionKeys) {
          if (partitionKey instanceof RexInputRef &&
                  ((RexInputRef)partitionKey).getIndex() >= groupingFields) {
            windowingExprsDigestToNodes.put(partitionKey.toString(), partitionKey);
          }
        }
        // Order keys
        for (RexFieldCollation orderKey : rexOverNode.getWindow().orderKeys) {
          if (orderKey.left instanceof RexInputRef &&
                  ((RexInputRef)orderKey.left).getIndex() >= groupingFields) {
            windowingExprsDigestToNodes.put(orderKey.left.toString(), orderKey.left);
          }
        }
      } else {
        projectExprsDigest.add(r.toString());
      }
    }

    // 2. We check whether there is a column needed by the
    //    windowing operation that is missing in the
    //    project expressions. For instance, if the windowing
    //    operation is over an aggregation column, Hive expects
    //    that column to be in the Select clause of the query.
    //    The idea is that if there is a column missing, we will
    //    replace the old project operator by two new project
    //    operators:
    //    - a project operator containing the original columns
    //      of the project operator plus all the columns that were
    //      missing
    //    - a project on top of the previous one, that will take
    //      out the columns that were missing and were added by the
    //      previous project

    // These data structures are needed to create the new project
    // operator (below)
    final List<RexNode> belowProjectExprs = new ArrayList<RexNode>();
    final List<String> belowProjectColumnNames = new ArrayList<String>();

    // This data structure is needed to create the new project
    // operator (top)
    final List<RexNode> topProjectExprs = new ArrayList<RexNode>();

    final int projectCount = project.getProjects().size();
    for (int i = 0; i < projectCount; i++) {
      belowProjectExprs.add(project.getProjects().get(i));
      belowProjectColumnNames.add(project.getRowType().getFieldNames().get(i));
      topProjectExprs.add(RexInputRef.of(i, project.getRowType()));
    }
    boolean windowingFix = false;
    for (Entry<String, RexNode> windowingExpr : windowingExprsDigestToNodes.entrySet()) {
      if (!projectExprsDigest.contains(windowingExpr.getKey())) {
        windowingFix = true;
        belowProjectExprs.add(windowingExpr.getValue());
        int colIndex = 0;
        String alias = "window_col_" + colIndex;
        while (belowProjectColumnNames.contains(alias)) {
          alias = "window_col_" + (colIndex++);
        }
        belowProjectColumnNames.add(alias);
      }
    }

    if (!windowingFix) {
      // We do not need to do anything, we bail out
      return;
    }

    // 3. We need to fix it, we create the two replacement project
    //    operators
    RelNode newProjectRel = projectFactory.createProject(
        aggregate, Collections.emptyList(), belowProjectExprs, belowProjectColumnNames);
    RelNode newTopProjectRel = projectFactory.createProject(
        newProjectRel, Collections.emptyList(), topProjectExprs, project.getRowType().getFieldNames());

    call.transformTo(newTopProjectRel);
  }
Rule to fix windowing issue when it is done over
aggregation columns (more info in HIVE-10627).

This rule is applied as a post-processing step after
optimization by Calcite in order to add columns
that may be pruned by RelFieldTrimmer, but are
still needed due to the concrete implementation of
Windowing processing in Hive.
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveWindowingFixRule.java