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)));
}