private void createAggImpl()

in core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java [3528:3730]


  private void createAggImpl(Blackboard bb,
      final AggConverter aggConverter,
      List<SqlNode> selectList,
      List<SqlNode> groupList,
      @Nullable SqlNode having,
      List<SqlNode> orderExprList,
      List<SqlNode> extraList) {
    // Find aggregate functions in SELECT and HAVING clause
    final AggregateFinder aggregateFinder = new AggregateFinder();
    aggregateFinder.visitAll(selectList);
    if (having != null) {
      having.accept(aggregateFinder);
    }

    // first replace the sub-queries inside the aggregates
    // because they will provide input rows to the aggregates.
    aggregateFinder.list.forEach(e ->
        replaceSubQueries(bb, e, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN));

    // also replace sub-queries inside filters in the aggregates
    aggregateFinder.filterList.forEach(e ->
        replaceSubQueries(bb, e, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN));

    // also replace sub-queries inside ordering spec in the aggregates
    aggregateFinder.orderList.forEach(e ->
        replaceSubQueries(bb, e, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN));

    // also replace sub-queries inside GROUP BY
    groupList.forEach(e ->
        replaceSubQueries(bb, e, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN));

    // register the group exprs

    // build a map to remember the projections from the top scope to the
    // output of the current root.
    //
    // Calcite allows expressions, not just column references in
    // group by list. This is not SQL 2003 compliant, but hey.

    final AggregatingSelectScope.Resolved r = aggConverter.getResolved();
    for (SqlNode e : r.groupExprList) {
      aggConverter.addGroupExpr(e);
    }

    final RexNode havingExpr;
    final PairList<RexNode, String> projects = PairList.of();

    try {
      checkArgument(bb.agg == null, "already in agg mode");
      bb.agg = aggConverter;

      // convert the select and having expressions, so that the
      // agg converter knows which aggregations are required

      for (SqlNode expr : selectList) {
        expr.accept(aggConverter);
        // Assert we don't have dangling items left in the stack
        assert !aggConverter.inOver;
      }
      for (SqlNode expr : orderExprList) {
        expr.accept(aggConverter);
        assert !aggConverter.inOver;
      }
      if (having != null) {
        having.accept(aggConverter);
        assert !aggConverter.inOver;
      }
      for (SqlNode expr : extraList) {
        expr.accept(aggConverter);
        assert !aggConverter.inOver;
      }

      // compute inputs to the aggregator
      final PairList<RexNode, @Nullable String> preExprs;
      if (aggConverter.convertedInputExprs.isEmpty()) {
        // Special case for COUNT(*), where we can end up with no inputs
        // at all.  The rest of the system doesn't like 0-tuples, so we
        // select a dummy constant here.
        final RexNode zero = rexBuilder.makeExactLiteral(BigDecimal.ZERO);
        preExprs = PairList.of(zero, null);
      } else {
        preExprs = aggConverter.convertedInputExprs;
      }

      final RelNode inputRel = bb.root();

      // Project the expressions required by agg and having.
      RelNode intermediateProject = relBuilder.push(inputRel)
          .projectNamed(preExprs.leftList(), preExprs.rightList(), false)
          .build();
      final RelNode r2;
      // deal with correlation
      final CorrelationUse p = getCorrelationUse(bb, intermediateProject);
      if (p != null) {
        assert p.r instanceof Project;
        // correlation variables have been normalized in p.r, we should use expressions
        // in p.r instead of the original exprs
        Project project1 = (Project) p.r;
        r2 = relBuilder.push(bb.root())
            .projectNamed(project1.getProjects(), project1.getRowType().getFieldNames(),
                true, ImmutableSet.of(p.id))
            .build();
      } else {
        r2 = intermediateProject;
      }
      bb.setRoot(r2, false);
      bb.mapRootRelToFieldProjection.put(bb.root(), r.groupExprProjection);

      // REVIEW jvs 31-Oct-2007:  doesn't the declaration of
      // monotonicity here assume sort-based aggregation at
      // the physical level?

      // Tell bb which of group columns are sorted.
      bb.columnMonotonicities.clear();
      groupList.forEach(e ->
          bb.columnMonotonicities.add(
              bb.scope.getMonotonicity(e)));

      // Add the aggregator
      bb.setRoot(
          createAggregate(bb, r.groupSet, r.groupSets.asList(),
              aggConverter.aggCalls), false);
      bb.mapRootRelToFieldProjection.put(bb.root(), r.groupExprProjection);

      // Replace sub-queries in having here and modify having to use
      // the replaced expressions
      if (having != null) {
        SqlNode newHaving = pushDownNotForIn(bb.scope, having);
        replaceSubQueries(bb, newHaving, RelOptUtil.Logic.UNKNOWN_AS_FALSE);
        havingExpr = bb.convertExpression(newHaving);
      } else {
        havingExpr = relBuilder.literal(true);
      }

      // Now convert the other sub-queries in the select list.
      // This needs to be done separately from the sub-query inside
      // any aggregate in the select list, and after the aggregate rel
      // is allocated.
      selectList.forEach(e ->
          replaceSubQueries(bb, e, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN));

      // Now sub-queries in the entire select list have been converted.
      // Convert the select expressions to get the final list to be
      // projected.
      int k = 0;

      // For select expressions, use the field names previously assigned
      // by the validator. If we derive afresh, we might generate names
      // like "EXPR$2" that don't match the names generated by the
      // validator. This is especially the case when there are system
      // fields; system fields appear in the relnode's rowtype but do not
      // (yet) appear in the validator type.
      final SelectScope selectScope =
          requireNonNull(SqlValidatorUtil.getEnclosingSelectScope(bb.scope));
      final SqlValidatorNamespace selectNamespace =
          getNamespace(selectScope.getNode());
      final List<String> names =
          selectNamespace.getRowType().getFieldNames();
      int sysFieldCount = selectList.size() - names.size();
      for (SqlNode expr : selectList) {
        projects.add(bb.convertExpression(expr),
            k < sysFieldCount
                ? SqlValidatorUtil.alias(expr, k++)
                : names.get(k++ - sysFieldCount));
      }

      for (SqlNode expr : orderExprList) {
        projects.add(bb.convertExpression(expr),
            SqlValidatorUtil.alias(expr, k++));
      }
      for (SqlNode expr : extraList) {
        projects.add(bb.convertExpression(expr),
            SqlValidatorUtil.alias(expr, k++));
      }
    } finally {
      bb.agg = null;
    }

    // implement HAVING (we have already checked that it is non-trivial)
    relBuilder.push(bb.root());
    // Set the correlation variables used in this sub-query to the filter node,
    // same logic is being used for the filter generated in where clause.
    Set<CorrelationId> variableSet = new HashSet<>();
    RexSubQuery subQ = RexUtil.SubQueryFinder.find(havingExpr);
    if (subQ != null) {
      CorrelationUse p = getCorrelationUse(bb, subQ.rel);
      if (p != null) {
        variableSet.add(p.id);
      }
    }
    relBuilder.filter(variableSet, havingExpr);

    // implement the SELECT list
    relBuilder.project(projects.leftList(), projects.rightList())
        .rename(projects.rightList());
    bb.setRoot(relBuilder.build(), false);

    // Tell bb which of group columns are sorted.
    bb.columnMonotonicities.clear();
    selectList.forEach(e ->
        bb.columnMonotonicities.add(
            bb.scope.getMonotonicity(e)));
  }