Skip to content

Commit

Permalink
[MXP-2548](https://jira.tools.sap/browse/MXP-2548) Dont optimize proj…
Browse files Browse the repository at this point in the history
…ections in aggregations when calc view table hint is present

Signed-off-by: Corvin Kuebler <[email protected]>
  • Loading branch information
CoKueb committed Jul 22, 2024
1 parent 43d5825 commit 17c4312
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 28 deletions.
22 changes: 22 additions & 0 deletions core/src/main/java/org/apache/calcite/plan/Hints.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.calcite.plan;

public class Hints {
public static final String CALCULATION_VIEW = "CALCULATION_VIEW";
}
16 changes: 16 additions & 0 deletions core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,22 @@ public static RelNode createCastRel(
rel, castRowType, rename, RelFactories.DEFAULT_PROJECT_FACTORY);
}

public static boolean hasCalcViewHint(RelNode rel) {
boolean hasCalcViewHint = false;
for (RelNode input : rel.getInputs()) {
input = input.stripped();
if (input instanceof TableScan) {
hasCalcViewHint = ((TableScan) input).getHints().stream().anyMatch(it -> it.hintName.equals(Hints.CALCULATION_VIEW));
} else {
hasCalcViewHint = hasCalcViewHint(input);
}
if (hasCalcViewHint) {
return hasCalcViewHint;
}
}
return hasCalcViewHint;
}

/**
* Creates a projection which casts a rel's output to a desired row type.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.plan.RelOptSamplingParameters;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelFieldCollation;
Expand Down Expand Up @@ -608,6 +609,7 @@ protected void buildAggGroupList(Aggregate e, Builder builder,
*/
protected Builder buildAggregate(Aggregate e, Builder builder,
List<SqlNode> selectList, List<SqlNode> groupByList) {
boolean isCountStar = false;
for (AggregateCall aggCall : e.getAggCallList()) {
SqlNode aggCallSqlNode = builder.context.toSql(aggCall);
RelDataType aggCallRelDataType = aggCall.getType();
Expand All @@ -616,8 +618,21 @@ protected Builder buildAggregate(Aggregate e, Builder builder,
} else if (aggCall.getAggregation() instanceof SqlMinMaxAggFunction) {
aggCallSqlNode = dialect.rewriteMaxMinExpr(aggCallSqlNode, aggCallRelDataType);
}
isCountStar = (RelOptUtil.hasCalcViewHint(e) && selectList.isEmpty() && aggCall.getAggregation().getKind() == SqlKind.COUNT);
addSelect(selectList, aggCallSqlNode, e.getRowType());
}
if (isCountStar) {
SqlSelect oldSelect = builder.select;
boolean isStar = false;
if (oldSelect.getSelectList().size() == 1 && oldSelect.getSelectList().get(0) instanceof SqlIdentifier) {
isStar = ((SqlIdentifier) oldSelect.getSelectList().get(0)).isStar();
}

if (!isStar) {
builder.setFrom(oldSelect.clone(oldSelect.getParserPosition()));
}
}

builder.setSelect(new SqlNodeList(selectList, POS));
if (!groupByList.isEmpty() || e.getAggCallList().isEmpty()) {
// Some databases don't support "GROUP BY ()". We can omit it as long
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2279,6 +2279,10 @@ public void setSelect(SqlNodeList nodeList) {
select.setSelectList(nodeList);
}

public void setFrom(SqlNode node) {
select.setFrom(node);
}

public void setWhere(SqlNode node) {
assert clauses.contains(Clause.WHERE);
select.setWhere(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,7 @@
import org.apache.calcite.rel.RelRoot;
import org.apache.calcite.rel.RelShuttleImpl;
import org.apache.calcite.rel.SingleRel;
import org.apache.calcite.rel.core.Aggregate;
import org.apache.calcite.rel.core.AggregateCall;
import org.apache.calcite.rel.core.Collect;
import org.apache.calcite.rel.core.CorrelationId;
import org.apache.calcite.rel.core.Filter;
import org.apache.calcite.rel.core.Join;
import org.apache.calcite.rel.core.JoinInfo;
import org.apache.calcite.rel.core.JoinRelType;
import org.apache.calcite.rel.core.Project;
import org.apache.calcite.rel.core.RelFactories;
import org.apache.calcite.rel.core.Sort;
import org.apache.calcite.rel.core.*;
import org.apache.calcite.rel.hint.HintStrategyTable;
import org.apache.calcite.rel.hint.Hintable;
import org.apache.calcite.rel.hint.RelHint;
Expand Down Expand Up @@ -3518,24 +3508,39 @@ protected final void createAggImpl(

// 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);
if (bb.root != null && RelOptUtil.hasCalcViewHint(bb.root)) {
if (!aggConverter.convertedInputExprs.isEmpty()) {
preExprs = aggConverter.convertedInputExprs;
final RelNode inputRel = bb.root();
bb.setRoot(
relBuilder.push(inputRel)
.projectNamed(preExprs.leftList(), preExprs.rightList(), false)
.build(),
false);
}
} else {
preExprs = aggConverter.convertedInputExprs;
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.
bb.setRoot(
relBuilder.push(inputRel)
.projectNamed(preExprs.leftList(), preExprs.rightList(), false)
.build(),
false);
}

final RelNode inputRel = bb.root();

// Project the expressions required by agg and having.
bb.setRoot(
relBuilder.push(inputRel)
.projectNamed(preExprs.leftList(), preExprs.rightList(), false)
.build(),
false);
bb.mapRootRelToFieldProjection.put(bb.root(), r.groupExprProjection);

// REVIEW jvs 31-Oct-2007: doesn't the declaration of
Expand Down
11 changes: 7 additions & 4 deletions core/src/main/java/org/apache/calcite/tools/RelBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -2362,7 +2362,7 @@ private RelBuilder aggregate_(GroupKeyImpl groupKey,
final ImmutableList<AggCallPlus> aggCalls) {
if (groupKey.nodes.isEmpty()
&& aggCalls.isEmpty()
&& config.pruneInputOfAggregate()) {
&& !(stack.peek() != null && RelOptUtil.hasCalcViewHint(stack.peek().rel))) {
// Query is "SELECT /* no fields */ FROM t GROUP BY ()", which always
// returns one row with zero columns.
if (config.preventEmptyFieldList()) {
Expand Down Expand Up @@ -2442,7 +2442,7 @@ private RelBuilder aggregate_(GroupKeyImpl groupKey,
}

return pruneAggregateInputFieldsAndDeduplicateAggCalls(r, groupSet, groupSets, aggregateCalls,
frame.fields, registrar.extraNodes);
frame.fields, registrar.extraNodes, RelOptUtil.hasCalcViewHint(r));
}

/**
Expand All @@ -2454,10 +2454,12 @@ private RelBuilder pruneAggregateInputFieldsAndDeduplicateAggCalls(
final ImmutableList<ImmutableBitSet> groupSets,
final List<AggregateCall> aggregateCalls,
PairList<ImmutableSet<String>, RelDataTypeField> inFields,
final List<RexNode> extraNodes) {
final List<RexNode> extraNodes,
boolean hasCalViewHint) {
final ImmutableBitSet groupSetAfterPruning;
final ImmutableList<ImmutableBitSet> groupSetsAfterPruning;
if (config.pruneInputOfAggregate()

if (!hasCalViewHint
&& r instanceof Project) {
final Set<Integer> fieldsUsed =
RelOptUtil.getAllFields2(groupSet, aggregateCalls);
Expand Down Expand Up @@ -2565,6 +2567,7 @@ private AggregateCall removeRedundantAggregateDistinct(
return aggregateCall;
}


/** Returns whether an input is already unique, and therefore a Project
* can be created instead of an Aggregate.
*
Expand Down

0 comments on commit 17c4312

Please sign in to comment.